var global = this;
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.jade=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
    'use strict';

    var nodes = require('./nodes');
    var filters = require('./filters');
    var doctypes = require('./doctypes');
    var runtime = require('./runtime');
    var utils = require('./utils');
    var selfClosing = require('void-elements');
    var parseJSExpression = require('character-parser').parseMax;
    var constantinople = require('constantinople');

    function isConstant(src) {
        return constantinople(src, {jade: runtime, 'jade_interp': undefined});
    }
    function toConstant(src) {
        return constantinople.toConstant(src, {jade: runtime, 'jade_interp': undefined});
    }
    function errorAtNode(node, error) {
        error.line = node.line;
        error.filename = node.filename;
        return error;
    }

    /**
     * Initialize `Compiler` with the given `node`.
     *
     * @param {Node} node
     * @param {Object} options
     * @api public
     */

    var Compiler = module.exports = function Compiler(node, options) {
        this.options = options = options || {};
        this.node = node;
        this.hasCompiledDoctype = false;
        this.hasCompiledTag = false;
        this.pp = options.pretty || false;
        if (this.pp && typeof this.pp !== 'string') {
            this.pp = '  ';
        }
        this.debug = false !== options.compileDebug;
        this.indents = 0;
        this.parentIndents = 0;
        this.terse = false;
        this.mixins = {};
        this.dynamicMixins = false;
        if (options.doctype) this.setDoctype(options.doctype);
    };

    /**
     * Compiler prototype.
     */

    Compiler.prototype = {

        /**
         * Compile parse tree to JavaScript.
         *
         * @api public
         */

        compile: function(){
            this.buf = [];
            if (this.pp) this.buf.push("var jade_indent = [];");
            this.lastBufferedIdx = -1;
            this.visit(this.node);
            if (!this.dynamicMixins) {
                // if there are no dynamic mixins we can remove any un-used mixins
                var mixinNames = Object.keys(this.mixins);
                for (var i = 0; i < mixinNames.length; i++) {
                    var mixin = this.mixins[mixinNames[i]];
                    if (!mixin.used) {
                        for (var x = 0; x < mixin.instances.length; x++) {
                            for (var y = mixin.instances[x].start; y < mixin.instances[x].end; y++) {
                                this.buf[y] = '';
                            }
                        }
                    }
                }
            }
            return this.buf.join('\n');
        },

        /**
         * Sets the default doctype `name`. Sets terse mode to `true` when
         * html 5 is used, causing self-closing tags to end with ">" vs "/>",
         * and boolean attributes are not mirrored.
         *
         * @param {string} name
         * @api public
         */

        setDoctype: function(name){
            this.doctype = doctypes[name.toLowerCase()] || '<!DOCTYPE ' + name + '>';
            this.terse = this.doctype.toLowerCase() == '<!doctype html>';
            this.xml = 0 == this.doctype.indexOf('<?xml');
        },

        /**
         * Buffer the given `str` exactly as is or with interpolation
         *
         * @param {String} str
         * @param {Boolean} interpolate
         * @api public
         */

        buffer: function (str, interpolate) {
            var self = this;
            if (interpolate) {
                var match = /(\\)?([#!]){((?:.|\n)*)$/.exec(str);
                if (match) {
                    this.buffer(str.substr(0, match.index), false);
                    if (match[1]) { // escape
                        this.buffer(match[2] + '{', false);
                        this.buffer(match[3], true);
                        return;
                    } else {
                        var rest = match[3];
                        var range = parseJSExpression(rest);
                        var code = ('!' == match[2] ? '' : 'jade.escape') + "((jade_interp = " + range.src + ") == null ? '' : jade_interp)";
                        this.bufferExpression(code);
                        this.buffer(rest.substr(range.end + 1), true);
                        return;
                    }
                }
            }

            str = utils.stringify(str);
            str = str.substr(1, str.length - 2);

            if (this.lastBufferedIdx == this.buf.length) {
                if (this.lastBufferedType === 'code') this.lastBuffered += ' + "';
                this.lastBufferedType = 'text';
                this.lastBuffered += str;
                this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + '");'
            } else {
                this.buf.push('buf.push("' + str + '");');
                this.lastBufferedType = 'text';
                this.bufferStartChar = '"';
                this.lastBuffered = str;
                this.lastBufferedIdx = this.buf.length;
            }
        },

        /**
         * Buffer the given `src` so it is evaluated at run time
         *
         * @param {String} src
         * @api public
         */

        bufferExpression: function (src) {
            if (isConstant(src)) {
                return this.buffer(toConstant(src) + '', false)
            }
            if (this.lastBufferedIdx == this.buf.length) {
                if (this.lastBufferedType === 'text') this.lastBuffered += '"';
                this.lastBufferedType = 'code';
                this.lastBuffered += ' + (' + src + ')';
                this.buf[this.lastBufferedIdx - 1] = 'buf.push(' + this.bufferStartChar + this.lastBuffered + ');'
            } else {
                this.buf.push('buf.push(' + src + ');');
                this.lastBufferedType = 'code';
                this.bufferStartChar = '';
                this.lastBuffered = '(' + src + ')';
                this.lastBufferedIdx = this.buf.length;
            }
        },

        /**
         * Buffer an indent based on the current `indent`
         * property and an additional `offset`.
         *
         * @param {Number} offset
         * @param {Boolean} newline
         * @api public
         */

        prettyIndent: function(offset, newline){
            offset = offset || 0;
            newline = newline ? '\n' : '';
            this.buffer(newline + Array(this.indents + offset).join(this.pp));
            if (this.parentIndents)
                this.buf.push("buf.push.apply(buf, jade_indent);");
        },

        /**
         * Visit `node`.
         *
         * @param {Node} node
         * @api public
         */

        visit: function(node){
            var debug = this.debug;

            if (debug) {
                this.buf.push('jade_debug.unshift({ lineno: ' + node.line
                + ', filename: ' + (node.filename
                    ? utils.stringify(node.filename)
                    : 'jade_debug[0].filename')
                + ' });');
            }

            // Massive hack to fix our context
            // stack for - else[ if] etc
            if (false === node.debug && this.debug) {
                this.buf.pop();
                this.buf.pop();
            }

            this.visitNode(node);

            if (debug) this.buf.push('jade_debug.shift();');
        },

        /**
         * Visit `node`.
         *
         * @param {Node} node
         * @api public
         */

        visitNode: function(node){
            return this['visit' + node.type](node);
        },

        /**
         * Visit case `node`.
         *
         * @param {Literal} node
         * @api public
         */

        visitCase: function(node){
            var _ = this.withinCase;
            this.withinCase = true;
            this.buf.push('switch (' + node.expr + '){');
            this.visit(node.block);
            this.buf.push('}');
            this.withinCase = _;
        },

        /**
         * Visit when `node`.
         *
         * @param {Literal} node
         * @api public
         */

        visitWhen: function(node){
            if ('default' == node.expr) {
                this.buf.push('default:');
            } else {
                this.buf.push('case ' + node.expr + ':');
            }
            if (node.block) {
                this.visit(node.block);
                this.buf.push('  break;');
            }
        },

        /**
         * Visit literal `node`.
         *
         * @param {Literal} node
         * @api public
         */

        visitLiteral: function(node){
            this.buffer(node.str);
        },

        /**
         * Visit all nodes in `block`.
         *
         * @param {Block} block
         * @api public
         */

        visitBlock: function(block){
            var len = block.nodes.length
                , escape = this.escape
                , pp = this.pp

            // Pretty print multi-line text
            if (pp && len > 1 && !escape && block.nodes[0].isText && block.nodes[1].isText)
                this.prettyIndent(1, true);

            for (var i = 0; i < len; ++i) {
                // Pretty print text
                if (pp && i > 0 && !escape && block.nodes[i].isText && block.nodes[i-1].isText)
                    this.prettyIndent(1, false);

                this.visit(block.nodes[i]);
                // Multiple text nodes are separated by newlines
                if (block.nodes[i+1] && block.nodes[i].isText && block.nodes[i+1].isText)
                    this.buffer('\n');
            }
        },

        /**
         * Visit a mixin's `block` keyword.
         *
         * @param {MixinBlock} block
         * @api public
         */

        visitMixinBlock: function(block){
            if (this.pp) this.buf.push("jade_indent.push('" + Array(this.indents + 1).join(this.pp) + "');");
            this.buf.push('block && block();');
            if (this.pp) this.buf.push("jade_indent.pop();");
        },

        /**
         * Visit `doctype`. Sets terse mode to `true` when html 5
         * is used, causing self-closing tags to end with ">" vs "/>",
         * and boolean attributes are not mirrored.
         *
         * @param {Doctype} doctype
         * @api public
         */

        visitDoctype: function(doctype){
            if (doctype && (doctype.val || !this.doctype)) {
                this.setDoctype(doctype.val || 'default');
            }

            if (this.doctype) this.buffer(this.doctype);
            this.hasCompiledDoctype = true;
        },

        /**
         * Visit `mixin`, generating a function that
         * may be called within the template.
         *
         * @param {Mixin} mixin
         * @api public
         */

        visitMixin: function(mixin){
            var name = 'jade_mixins[';
            var args = mixin.args || '';
            var block = mixin.block;
            var attrs = mixin.attrs;
            var attrsBlocks = mixin.attributeBlocks.slice();
            var pp = this.pp;
            var dynamic = mixin.name[0]==='#';
            var key = mixin.name;
            if (dynamic) this.dynamicMixins = true;
            name += (dynamic ? mixin.name.substr(2,mixin.name.length-3):'"'+mixin.name+'"')+']';

            this.mixins[key] = this.mixins[key] || {used: false, instances: []};
            if (mixin.call) {
                this.mixins[key].used = true;
                if (pp) this.buf.push("jade_indent.push('" + Array(this.indents + 1).join(pp) + "');")
                if (block || attrs.length || attrsBlocks.length) {

                    this.buf.push(name + '.call({');

                    if (block) {
                        this.buf.push('block: function(){');

                        // Render block with no indents, dynamically added when rendered
                        this.parentIndents++;
                        var _indents = this.indents;
                        this.indents = 0;
                        this.visit(mixin.block);
                        this.indents = _indents;
                        this.parentIndents--;

                        if (attrs.length || attrsBlocks.length) {
                            this.buf.push('},');
                        } else {
                            this.buf.push('}');
                        }
                    }

                    if (attrsBlocks.length) {
                        if (attrs.length) {
                            var val = this.attrs(attrs);
                            attrsBlocks.unshift(val);
                        }
                        this.buf.push('attributes: jade.merge([' + attrsBlocks.join(',') + '])');
                    } else if (attrs.length) {
                        var val = this.attrs(attrs);
                        this.buf.push('attributes: ' + val);
                    }

                    if (args) {
                        this.buf.push('}, ' + args + ');');
                    } else {
                        this.buf.push('});');
                    }

                } else {
                    this.buf.push(name + '(' + args + ');');
                }
                if (pp) this.buf.push("jade_indent.pop();")
            } else {
                var mixin_start = this.buf.length;
                args = args ? args.split(',') : [];
                var rest;
                if (args.length && /^\.\.\./.test(args[args.length - 1].trim())) {
                    rest = args.pop().trim().replace(/^\.\.\./, '');
                }
                this.buf.push(name + ' = function(' + args.join(',') + '){');
                this.buf.push('var block = (this && this.block), attributes = (this && this.attributes) || {};');
                if (rest) {
                    this.buf.push('var ' + rest + ' = [];');
                    this.buf.push('for (jade_interp = ' + args.length + '; jade_interp < arguments.length; jade_interp++) {');
                    this.buf.push('  ' + rest + '.push(arguments[jade_interp]);');
                    this.buf.push('}');
                }
                this.parentIndents++;
                this.visit(block);
                this.parentIndents--;
                this.buf.push('};');
                var mixin_end = this.buf.length;
                this.mixins[key].instances.push({start: mixin_start, end: mixin_end});
            }
        },

        /**
         * Visit `tag` buffering tag markup, generating
         * attributes, visiting the `tag`'s code and block.
         *
         * @param {Tag} tag
         * @api public
         */

        visitTag: function(tag){
            this.indents++;
            var name = tag.name
                , pp = this.pp
                , self = this;

            function bufferName() {
                if (tag.buffer) self.bufferExpression(name);
                else self.buffer(name);
            }

            if ('pre' == tag.name) this.escape = true;

            if (!this.hasCompiledTag) {
                if (!this.hasCompiledDoctype && 'html' == name) {
                    this.visitDoctype();
                }
                this.hasCompiledTag = true;
            }

            // pretty print
            if (pp && !tag.isInline())
                this.prettyIndent(0, true);

            if (tag.selfClosing || (!this.xml && selfClosing[tag.name])) {
                this.buffer('<');
                bufferName();
                this.visitAttributes(tag.attrs, tag.attributeBlocks.slice());
                this.terse
                    ? this.buffer('>')
                    : this.buffer('/>');
                // if it is non-empty throw an error
                if (tag.block &&
                    !(tag.block.type === 'Block' && tag.block.nodes.length === 0) &&
                    tag.block.nodes.some(function (tag) {
                        return tag.type !== 'Text' || !/^\s*$/.test(tag.val)
                    })) {
                    throw errorAtNode(tag, new Error(name + ' is self closing and should not have content.'));
                }
            } else {
                // Optimize attributes buffering
                this.buffer('<');
                bufferName();
                this.visitAttributes(tag.attrs, tag.attributeBlocks.slice());
                this.buffer('>');
                if (tag.code) this.visitCode(tag.code);
                this.visit(tag.block);

                // pretty print
                if (pp && !tag.isInline() && 'pre' != tag.name && !tag.canInline())
                    this.prettyIndent(0, true);

                this.buffer('</');
                bufferName();
                this.buffer('>');
            }

            if ('pre' == tag.name) this.escape = false;

            this.indents--;
        },

        /**
         * Visit `filter`, throwing when the filter does not exist.
         *
         * @param {Filter} filter
         * @api public
         */

        visitFilter: function(filter){
            var text = filter.block.nodes.map(
                function(node){ return node.val; }
            ).join('\n');
            filter.attrs.filename = this.options.filename;
            try {
                this.buffer(filters(filter.name, text, filter.attrs), true);
            } catch (err) {
                throw errorAtNode(filter, err);
            }
        },

        /**
         * Visit `text` node.
         *
         * @param {Text} text
         * @api public
         */

        visitText: function(text){
            this.buffer(text.val, true);
        },

        /**
         * Visit a `comment`, only buffering when the buffer flag is set.
         *
         * @param {Comment} comment
         * @api public
         */

        visitComment: function(comment){
            if (!comment.buffer) return;
            if (this.pp) this.prettyIndent(1, true);
            this.buffer('<!--' + comment.val + '-->');
        },

        /**
         * Visit a `BlockComment`.
         *
         * @param {Comment} comment
         * @api public
         */

        visitBlockComment: function(comment){
            if (!comment.buffer) return;
            if (this.pp) this.prettyIndent(1, true);
            this.buffer('<!--' + comment.val);
            this.visit(comment.block);
            if (this.pp) this.prettyIndent(1, true);
            this.buffer('-->');
        },

        /**
         * Visit `code`, respecting buffer / escape flags.
         * If the code is followed by a block, wrap it in
         * a self-calling function.
         *
         * @param {Code} code
         * @api public
         */

        visitCode: function(code){
            // Wrap code blocks with {}.
            // we only wrap unbuffered code blocks ATM
            // since they are usually flow control

            // Buffer code
            if (code.buffer) {
                var val = code.val.trim();
                val = 'null == (jade_interp = '+val+') ? "" : jade_interp';
                if (code.escape) val = 'jade.escape(' + val + ')';
                this.bufferExpression(val);
            } else {
                this.buf.push(code.val);
            }

            // Block support
            if (code.block) {
                if (!code.buffer) this.buf.push('{');
                this.visit(code.block);
                if (!code.buffer) this.buf.push('}');
            }
        },

        /**
         * Visit `each` block.
         *
         * @param {Each} each
         * @api public
         */

        visitEach: function(each){
            this.buf.push(''
            + '// iterate ' + each.obj + '\n'
            + ';(function(){\n'
            + '  var $$obj = ' + each.obj + ';\n'
            + '  if (\'number\' == typeof $$obj.length) {\n');

            if (each.alternative) {
                this.buf.push('  if ($$obj.length) {');
            }

            this.buf.push(''
            + '    for (var ' + each.key + ' = 0, $$l = $$obj.length; ' + each.key + ' < $$l; ' + each.key + '++) {\n'
            + '      var ' + each.val + ' = $$obj[' + each.key + '];\n');

            this.visit(each.block);

            this.buf.push('    }\n');

            if (each.alternative) {
                this.buf.push('  } else {');
                this.visit(each.alternative);
                this.buf.push('  }');
            }

            this.buf.push(''
            + '  } else {\n'
            + '    var $$l = 0;\n'
            + '    for (var ' + each.key + ' in $$obj) {\n'
            + '      $$l++;'
            + '      var ' + each.val + ' = $$obj[' + each.key + '];\n');

            this.visit(each.block);

            this.buf.push('    }\n');
            if (each.alternative) {
                this.buf.push('    if ($$l === 0) {');
                this.visit(each.alternative);
                this.buf.push('    }');
            }
            this.buf.push('  }\n}).call(this);\n');
        },

        /**
         * Visit `attrs`.
         *
         * @param {Array} attrs
         * @api public
         */

        visitAttributes: function(attrs, attributeBlocks){
            if (attributeBlocks.length) {
                if (attrs.length) {
                    var val = this.attrs(attrs);
                    attributeBlocks.unshift(val);
                }
                this.bufferExpression('jade.attrs(jade.merge([' + attributeBlocks.join(',') + ']), ' + utils.stringify(this.terse) + ')');
            } else if (attrs.length) {
                this.attrs(attrs, true);
            }
        },

        /**
         * Compile attributes.
         */

        attrs: function(attrs, buffer){
            var buf = [];
            var classes = [];
            var classEscaping = [];

            attrs.forEach(function(attr){
                var key = attr.name;
                var escaped = attr.escaped;

                if (key === 'class') {
                    classes.push(attr.val);
                    classEscaping.push(attr.escaped);
                } else if (isConstant(attr.val)) {
                    if (buffer) {
                        this.buffer(runtime.attr(key, toConstant(attr.val), escaped, this.terse));
                    } else {
                        var val = toConstant(attr.val);
                        if (key === 'style') val = runtime.style(val);
                        if (escaped && !(key.indexOf('data') === 0 && typeof val !== 'string')) {
                            val = runtime.escape(val);
                        }
                        buf.push(utils.stringify(key) + ': ' + utils.stringify(val));
                    }
                } else {
                    if (buffer) {
                        this.bufferExpression('jade.attr("' + key + '", ' + attr.val + ', ' + utils.stringify(escaped) + ', ' + utils.stringify(this.terse) + ')');
                    } else {
                        var val = attr.val;
                        if (key === 'style') {
                            val = 'jade.style(' + val + ')';
                        }
                        if (escaped && !(key.indexOf('data') === 0)) {
                            val = 'jade.escape(' + val + ')';
                        } else if (escaped) {
                            val = '(typeof (jade_interp = ' + val + ') == "string" ? jade.escape(jade_interp) : jade_interp)';
                        }
                        buf.push(utils.stringify(key) + ': ' + val);
                    }
                }
            }.bind(this));
            if (buffer) {
                if (classes.every(isConstant)) {
                    this.buffer(runtime.cls(classes.map(toConstant), classEscaping));
                } else {
                    this.bufferExpression('jade.cls([' + classes.join(',') + '], ' + utils.stringify(classEscaping) + ')');
                }
            } else if (classes.length) {
                if (classes.every(isConstant)) {
                    classes = utils.stringify(runtime.joinClasses(classes.map(toConstant).map(runtime.joinClasses).map(function (cls, i) {
                        return classEscaping[i] ? runtime.escape(cls) : cls;
                    })));
                } else {
                    classes = '(jade_interp = ' + utils.stringify(classEscaping) + ',' +
                    ' jade.joinClasses([' + classes.join(',') + '].map(jade.joinClasses).map(function (cls, i) {' +
                    '   return jade_interp[i] ? jade.escape(cls) : cls' +
                    ' }))' +
                    ')';
                }
                if (classes.length)
                    buf.push('"class": ' + classes);
            }
            return '{' + buf.join(',') + '}';
        }
    };

},{"./doctypes":2,"./filters":3,"./nodes":16,"./runtime":24,"./utils":25,"character-parser":29,"constantinople":30,"void-elements":34}],2:[function(require,module,exports){
    'use strict';

    module.exports = {
        'default': '<!DOCTYPE html>'
        , 'xml': '<?xml version="1.0" encoding="utf-8" ?>'
        , 'transitional': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">'
        , 'strict': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">'
        , 'frameset': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">'
        , '1.1': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">'
        , 'basic': '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML Basic 1.1//EN" "http://www.w3.org/TR/xhtml-basic/xhtml-basic11.dtd">'
        , 'mobile': '<!DOCTYPE html PUBLIC "-//WAPFORUM//DTD XHTML Mobile 1.2//EN" "http://www.openmobilealliance.org/tech/DTD/xhtml-mobile12.dtd">'
    };
},{}],3:[function(require,module,exports){
    'use strict';

    module.exports = filter;
    function filter(name, str, options) {
        if (typeof filter[name] === 'function') {
            return filter[name](str, options);
        } else {
            throw new Error('unknown filter ":' + name + '"');
        }
    }

},{}],4:[function(require,module,exports){
    'use strict';

    /*!
     * Jade
     * Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
     * MIT Licensed
     */

    /**
     * Module dependencies.
     */

    var Parser = require('./parser')
        , Lexer = require('./lexer')
        , Compiler = require('./compiler')
        , runtime = require('./runtime')
        , addWith = require('with')
        , fs = require('fs')
        , utils = require('./utils');

    /**
     * Expose self closing tags.
     */

// FIXME: either stop exporting selfClosing in v2 or export the new object
// form
    exports.selfClosing = Object.keys(require('void-elements'));

    /**
     * Default supported doctypes.
     */

    exports.doctypes = require('./doctypes');

    /**
     * Text filters.
     */

    exports.filters = require('./filters');

    /**
     * Utilities.
     */

    exports.utils = utils;

    /**
     * Expose `Compiler`.
     */

    exports.Compiler = Compiler;

    /**
     * Expose `Parser`.
     */

    exports.Parser = Parser;

    /**
     * Expose `Lexer`.
     */

    exports.Lexer = Lexer;

    /**
     * Nodes.
     */

    exports.nodes = require('./nodes');

    /**
     * Jade runtime helpers.
     */

    exports.runtime = runtime;

    /**
     * Template function cache.
     */

    exports.cache = {};

    /**
     * Parse the given `str` of jade and return a function body.
     *
     * @param {String} str
     * @param {Object} options
     * @return {Object}
     * @api private
     */

    function parse(str, options){

        if (options.lexer) {
            console.warn('Using `lexer` as a local in render() is deprecated and '
            + 'will be interpreted as an option in Jade 2.0.0');
        }

        // Parse
        var parser = new (options.parser || Parser)(str, options.filename, options);
        var tokens;
        try {
            // Parse
            tokens = parser.parse();
        } catch (err) {
            parser = parser.context();
            runtime.rethrow(err, parser.filename, parser.lexer.lineno, parser.input);
        }

        // Compile
        var compiler = new (options.compiler || Compiler)(tokens, options);
        var js;
        try {
            js = compiler.compile();
        } catch (err) {
            if (err.line && (err.filename || !options.filename)) {
                runtime.rethrow(err, err.filename, err.line, parser.input);
            } else {
                if (err instanceof Error) {
                    err.message += '\n\nPlease report this entire error and stack trace to https://github.com/jadejs/jade/issues';
                }
                throw err;
            }
        }

        // Debug compiler
        if (options.debug) {
            console.error('\nCompiled Function:\n\n\u001b[90m%s\u001b[0m', js.replace(/^/gm, '  '));
        }

        var globals = [];

        if (options.globals) {
            globals = options.globals.slice();
        }

        globals.push('jade');
        globals.push('jade_mixins');
        globals.push('jade_interp');
        globals.push('jade_debug');
        globals.push('buf');

        var body = ''
            + 'var buf = [];\n'
            + 'var jade_mixins = {};\n'
            + 'var jade_interp;\n'
            + (options.self
                ? 'var self = locals || {};\n' + js
                : addWith('locals || {}', '\n' + js, globals)) + ';'
            + 'return buf.join("");';
        return {body: body, dependencies: parser.dependencies};
    }

    /**
     * Get the template from a string or a file, either compiled on-the-fly or
     * read from cache (if enabled), and cache the template if needed.
     *
     * If `str` is not set, the file specified in `options.filename` will be read.
     *
     * If `options.cache` is true, this function reads the file from
     * `options.filename` so it must be set prior to calling this function.
     *
     * @param {Object} options
     * @param {String=} str
     * @return {Function}
     * @api private
     */
    function handleTemplateCache (options, str) {
        var key = options.filename;
        if (options.cache && exports.cache[key]) {
            return exports.cache[key];
        } else {
            if (str === undefined) str = fs.readFileSync(options.filename, 'utf8');
            var templ = exports.compile(str, options);
            if (options.cache) exports.cache[key] = templ;
            return templ;
        }
    }

    /**
     * Compile a `Function` representation of the given jade `str`.
     *
     * Options:
     *
     *   - `compileDebug` when `false` debugging code is stripped from the compiled
     template, when it is explicitly `true`, the source code is included in
     the compiled template for better accuracy.
     *   - `filename` used to improve errors when `compileDebug` is not `false` and to resolve imports/extends
     *
     * @param {String} str
     * @param {Options} options
     * @return {Function}
     * @api public
     */

    exports.compile = function(str, options){
        var options = options || {}
            , filename = options.filename
                ? utils.stringify(options.filename)
                : 'undefined'
            , fn;

        str = String(str);

        var parsed = parse(str, options);
        if (options.compileDebug !== false) {
            fn = [
                'var jade_debug = [{ lineno: 1, filename: ' + filename + ' }];'
                , 'try {'
                , parsed.body
                , '} catch (err) {'
                , '  jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno' + (options.compileDebug === true ? ',' + utils.stringify(str) : '') + ');'
                , '}'
            ].join('\n');
        } else {
            fn = parsed.body;
        }
        fn = new Function('locals, jade', fn)
        var res = function(locals){ return fn(locals, Object.create(runtime)) };
        if (options.client) {
            res.toString = function () {
                var err = new Error('The `client` option is deprecated, use the `jade.compileClient` method instead');
                err.name = 'Warning';
                console.error(err.stack || /* istanbul ignore next */ err.message);
                return exports.compileClient(str, options);
            };
        }
        res.dependencies = parsed.dependencies;
        return res;
    };

    /**
     * Compile a JavaScript source representation of the given jade `str`.
     *
     * Options:
     *
     *   - `compileDebug` When it is `true`, the source code is included in
     *     the compiled template for better error messages.
     *   - `filename` used to improve errors when `compileDebug` is not `true` and to resolve imports/extends
     *   - `name` the name of the resulting function (defaults to "template")
     *
     * @param {String} str
     * @param {Options} options
     * @return {Object}
     * @api public
     */

    exports.compileClientWithDependenciesTracked = function(str, options){
        var options = options || {};
        var name = options.name || 'template';
        var filename = options.filename ? utils.stringify(options.filename) : 'undefined';
        var fn;

        str = String(str);
        options.compileDebug = options.compileDebug ? true : false;
        var parsed = parse(str, options);
        if (options.compileDebug) {
            fn = [
                'var jade_debug = [{ lineno: 1, filename: ' + filename + ' }];'
                , 'try {'
                , parsed.body
                , '} catch (err) {'
                , '  jade.rethrow(err, jade_debug[0].filename, jade_debug[0].lineno, ' + utils.stringify(str) + ');'
                , '}'
            ].join('\n');
        } else {
            fn = parsed.body;
        }

        return {body: 'function ' + name + '(locals) {\n' + fn + '\n}', dependencies: parsed.dependencies};
    };

    /**
     * Compile a JavaScript source representation of the given jade `str`.
     *
     * Options:
     *
     *   - `compileDebug` When it is `true`, the source code is included in
     *     the compiled template for better error messages.
     *   - `filename` used to improve errors when `compileDebug` is not `true` and to resolve imports/extends
     *   - `name` the name of the resulting function (defaults to "template")
     *
     * @param {String} str
     * @param {Options} options
     * @return {String}
     * @api public
     */
    exports.compileClient = function (str, options) {
        return exports.compileClientWithDependenciesTracked(str, options).body;
    };

    /**
     * Compile a `Function` representation of the given jade file.
     *
     * Options:
     *
     *   - `compileDebug` when `false` debugging code is stripped from the compiled
     template, when it is explicitly `true`, the source code is included in
     the compiled template for better accuracy.
     *
     * @param {String} path
     * @param {Options} options
     * @return {Function}
     * @api public
     */
    exports.compileFile = function (path, options) {
        options = options || {};
        options.filename = path;
        return handleTemplateCache(options);
    };

    /**
     * Render the given `str` of jade.
     *
     * Options:
     *
     *   - `cache` enable template caching
     *   - `filename` filename required for `include` / `extends` and caching
     *
     * @param {String} str
     * @param {Object|Function} options or fn
     * @param {Function|undefined} fn
     * @returns {String}
     * @api public
     */

    exports.render = function(str, options, fn){
        // support callback API
        if ('function' == typeof options) {
            fn = options, options = undefined;
        }
        if (typeof fn === 'function') {
            var res
            try {
                res = exports.render(str, options);
            } catch (ex) {
                return fn(ex);
            }
            return fn(null, res);
        }

        options = options || {};

        // cache requires .filename
        if (options.cache && !options.filename) {
            throw new Error('the "filename" option is required for caching');
        }

        return handleTemplateCache(options, str)(options);
    };

    /**
     * Render a Jade file at the given `path`.
     *
     * @param {String} path
     * @param {Object|Function} options or callback
     * @param {Function|undefined} fn
     * @returns {String}
     * @api public
     */

    exports.renderFile = function(path, options, fn){
        // support callback API
        if ('function' == typeof options) {
            fn = options, options = undefined;
        }
        if (typeof fn === 'function') {
            var res
            try {
                res = exports.renderFile(path, options);
            } catch (ex) {
                return fn(ex);
            }
            return fn(null, res);
        }

        options = options || {};

        options.filename = path;
        return handleTemplateCache(options)(options);
    };


    /**
     * Compile a Jade file at the given `path` for use on the client.
     *
     * @param {String} path
     * @param {Object} options
     * @returns {String}
     * @api public
     */

    exports.compileFileClient = function(path, options){
        var key = path + ':client';
        options = options || {};

        options.filename = path;

        if (options.cache && exports.cache[key]) {
            return exports.cache[key];
        }

        var str = fs.readFileSync(options.filename, 'utf8');
        var out = exports.compileClient(str, options);
        if (options.cache) exports.cache[key] = out;
        return out;
    };

    /**
     * Express support.
     */

    exports.__express = exports.renderFile;

},{"./compiler":1,"./doctypes":2,"./filters":3,"./lexer":6,"./nodes":16,"./parser":23,"./runtime":24,"./utils":25,"fs":26,"void-elements":34,"with":35}],5:[function(require,module,exports){
    'use strict';

    module.exports = [
        'a'
        , 'abbr'
        , 'acronym'
        , 'b'
        , 'br'
        , 'code'
        , 'em'
        , 'font'
        , 'i'
        , 'img'
        , 'ins'
        , 'kbd'
        , 'map'
        , 'samp'
        , 'small'
        , 'span'
        , 'strong'
        , 'sub'
        , 'sup'
    ];
},{}],6:[function(require,module,exports){
    'use strict';

    var utils = require('./utils');
    var characterParser = require('character-parser');


    /**
     * Initialize `Lexer` with the given `str`.
     *
     * @param {String} str
     * @param {String} filename
     * @api private
     */

    var Lexer = module.exports = function Lexer(str, filename) {
        this.input = str.replace(/\r\n|\r/g, '\n');
        this.filename = filename;
        this.deferredTokens = [];
        this.lastIndents = 0;
        this.lineno = 1;
        this.stash = [];
        this.indentStack = [];
        this.indentRe = null;
        this.pipeless = false;
    };


    function assertExpression(exp) {
        //this verifies that a JavaScript expression is valid
        Function('', 'return (' + exp + ')');
    }
    function assertNestingCorrect(exp) {
        //this verifies that code is properly nested, but allows
        //invalid JavaScript such as the contents of `attributes`
        var res = characterParser(exp)
        if (res.isNesting()) {
            throw new Error('Nesting must match on expression `' + exp + '`')
        }
    }

    /**
     * Lexer prototype.
     */

    Lexer.prototype = {

        /**
         * Construct a token with the given `type` and `val`.
         *
         * @param {String} type
         * @param {String} val
         * @return {Object}
         * @api private
         */

        tok: function(type, val){
            return {
                type: type
                , line: this.lineno
                , val: val
            }
        },

        /**
         * Consume the given `len` of input.
         *
         * @param {Number} len
         * @api private
         */

        consume: function(len){
            this.input = this.input.substr(len);
        },

        /**
         * Scan for `type` with the given `regexp`.
         *
         * @param {String} type
         * @param {RegExp} regexp
         * @return {Object}
         * @api private
         */

        scan: function(regexp, type){
            var captures;
            if (captures = regexp.exec(this.input)) {
                this.consume(captures[0].length);
                return this.tok(type, captures[1]);
            }
        },

        /**
         * Defer the given `tok`.
         *
         * @param {Object} tok
         * @api private
         */

        defer: function(tok){
            this.deferredTokens.push(tok);
        },

        /**
         * Lookahead `n` tokens.
         *
         * @param {Number} n
         * @return {Object}
         * @api private
         */

        lookahead: function(n){
            var fetch = n - this.stash.length;
            while (fetch-- > 0) this.stash.push(this.next());
            return this.stash[--n];
        },

        /**
         * Return the indexOf `(` or `{` or `[` / `)` or `}` or `]` delimiters.
         *
         * @return {Number}
         * @api private
         */

        bracketExpression: function(skip){
            skip = skip || 0;
            var start = this.input[skip];
            if (start != '(' && start != '{' && start != '[') throw new Error('unrecognized start character');
            var end = ({'(': ')', '{': '}', '[': ']'})[start];
            var range = characterParser.parseMax(this.input, {start: skip + 1});
            if (this.input[range.end] !== end) throw new Error('start character ' + start + ' does not match end character ' + this.input[range.end]);
            return range;
        },

        /**
         * Stashed token.
         */

        stashed: function() {
            return this.stash.length
                && this.stash.shift();
        },

        /**
         * Deferred token.
         */

        deferred: function() {
            return this.deferredTokens.length
                && this.deferredTokens.shift();
        },

        /**
         * end-of-source.
         */

        eos: function() {
            if (this.input.length) return;
            if (this.indentStack.length) {
                this.indentStack.shift();
                return this.tok('outdent');
            } else {
                return this.tok('eos');
            }
        },

        /**
         * Blank line.
         */

        blank: function() {
            var captures;
            if (captures = /^\n *\n/.exec(this.input)) {
                this.consume(captures[0].length - 1);
                ++this.lineno;
                if (this.pipeless) return this.tok('text', '');
                return this.next();
            }
        },

        /**
         * Comment.
         */

        comment: function() {
            var captures;
            if (captures = /^\/\/(-)?([^\n]*)/.exec(this.input)) {
                this.consume(captures[0].length);
                var tok = this.tok('comment', captures[2]);
                tok.buffer = '-' != captures[1];
                this.pipeless = true;
                return tok;
            }
        },

        /**
         * Interpolated tag.
         */

        interpolation: function() {
            if (/^#\{/.test(this.input)) {
                var match = this.bracketExpression(1);

                this.consume(match.end + 1);
                return this.tok('interpolation', match.src);
            }
        },

        /**
         * Tag.
         */

        tag: function() {
            var captures;
            if (captures = /^(\w[-:\w]*)(\/?)/.exec(this.input)) {
                this.consume(captures[0].length);
                var tok, name = captures[1];
                if (':' == name[name.length - 1]) {
                    name = name.slice(0, -1);
                    tok = this.tok('tag', name);
                    this.defer(this.tok(':'));
                    if (this.input[0] !== ' ') {
                        console.warn('Warning: space required after `:` on line ' + this.lineno +
                        ' of jade file "' + this.filename + '"');
                    }
                    while (' ' == this.input[0]) this.input = this.input.substr(1);
                } else {
                    tok = this.tok('tag', name);
                }
                tok.selfClosing = !!captures[2];
                return tok;
            }
        },

        /**
         * Filter.
         */

        filter: function() {
            var tok = this.scan(/^:([\w\-]+)/, 'filter');
            if (tok) {
                this.pipeless = true;
                return tok;
            }
        },

        /**
         * Doctype.
         */

        doctype: function() {
            if (this.scan(/^!!! *([^\n]+)?/, 'doctype')) {
                throw new Error('`!!!` is deprecated, you must now use `doctype`');
            }
            var node = this.scan(/^(?:doctype) *([^\n]+)?/, 'doctype');
            if (node && node.val && node.val.trim() === '5') {
                throw new Error('`doctype 5` is deprecated, you must now use `doctype html`');
            }
            return node;
        },

        /**
         * Id.
         */

        id: function() {
            return this.scan(/^#([\w-]+)/, 'id');
        },

        /**
         * Class.
         */

        className: function() {
            return this.scan(/^\.([\w-]+)/, 'class');
        },

        /**
         * Text.
         */

        text: function() {
            return this.scan(/^(?:\| ?| )([^\n]+)/, 'text') ||
                this.scan(/^\|?( )/, 'text') ||
                this.scan(/^(<[^\n]*)/, 'text');
        },

        textFail: function () {
            var tok;
            if (tok = this.scan(/^([^\.\n][^\n]+)/, 'text')) {
                console.warn('Warning: missing space before text for line ' + this.lineno +
                ' of jade file "' + this.filename + '"');
                return tok;
            }
        },

        /**
         * Dot.
         */

        dot: function() {
            var match;
            if (match = this.scan(/^\./, 'dot')) {
                this.pipeless = true;
                return match;
            }
        },

        /**
         * Extends.
         */

        "extends": function() {
            return this.scan(/^extends? +([^\n]+)/, 'extends');
        },

        /**
         * Block prepend.
         */

        prepend: function() {
            var captures;
            if (captures = /^prepend +([^\n]+)/.exec(this.input)) {
                this.consume(captures[0].length);
                var mode = 'prepend'
                    , name = captures[1]
                    , tok = this.tok('block', name);
                tok.mode = mode;
                return tok;
            }
        },

        /**
         * Block append.
         */

        append: function() {
            var captures;
            if (captures = /^append +([^\n]+)/.exec(this.input)) {
                this.consume(captures[0].length);
                var mode = 'append'
                    , name = captures[1]
                    , tok = this.tok('block', name);
                tok.mode = mode;
                return tok;
            }
        },

        /**
         * Block.
         */

        block: function() {
            var captures;
            if (captures = /^block\b *(?:(prepend|append) +)?([^\n]+)/.exec(this.input)) {
                this.consume(captures[0].length);
                var mode = captures[1] || 'replace'
                    , name = captures[2]
                    , tok = this.tok('block', name);

                tok.mode = mode;
                return tok;
            }
        },

        /**
         * Mixin Block.
         */

        mixinBlock: function() {
            var captures;
            if (captures = /^block[ \t]*(\n|$)/.exec(this.input)) {
                this.consume(captures[0].length - captures[1].length);
                return this.tok('mixin-block');
            }
        },

        /**
         * Yield.
         */

        'yield': function() {
            return this.scan(/^yield */, 'yield');
        },

        /**
         * Include.
         */

        include: function() {
            return this.scan(/^include +([^\n]+)/, 'include');
        },

        /**
         * Include with filter
         */

        includeFiltered: function() {
            var captures;
            if (captures = /^include:([\w\-]+)([\( ])/.exec(this.input)) {
                this.consume(captures[0].length - 1);
                var filter = captures[1];
                var attrs = captures[2] === '(' ? this.attrs() : null;
                if (!(captures[2] === ' ' || this.input[0] === ' ')) {
                    throw new Error('expected space after include:filter but got ' + utils.stringify(this.input[0]));
                }
                captures = /^ *([^\n]+)/.exec(this.input);
                if (!captures || captures[1].trim() === '') {
                    throw new Error('missing path for include:filter');
                }
                this.consume(captures[0].length);
                var path = captures[1];
                var tok = this.tok('include', path);
                tok.filter = filter;
                tok.attrs = attrs;
                return tok;
            }
        },

        /**
         * Case.
         */

        "case": function() {
            return this.scan(/^case +([^\n]+)/, 'case');
        },

        /**
         * When.
         */

        when: function() {
            return this.scan(/^when +([^:\n]+)/, 'when');
        },

        /**
         * Default.
         */

        "default": function() {
            return this.scan(/^default */, 'default');
        },

        /**
         * Call mixin.
         */

        call: function(){

            var tok, captures;
            if (captures = /^\+(\s*)(([-\w]+)|(#\{))/.exec(this.input)) {
                // try to consume simple or interpolated call
                if (captures[3]) {
                    // simple call
                    this.consume(captures[0].length);
                    tok = this.tok('call', captures[3]);
                } else {
                    // interpolated call
                    var match = this.bracketExpression(2 + captures[1].length);
                    this.consume(match.end + 1);
                    assertExpression(match.src);
                    tok = this.tok('call', '#{'+match.src+'}');
                }

                // Check for args (not attributes)
                if (captures = /^ *\(/.exec(this.input)) {
                    var range = this.bracketExpression(captures[0].length - 1);
                    if (!/^\s*[-\w]+ *=/.test(range.src)) { // not attributes
                        this.consume(range.end + 1);
                        tok.args = range.src;
                    }
                    if (tok.args) {
                        assertExpression('[' + tok.args + ']');
                    }
                }

                return tok;
            }
        },

        /**
         * Mixin.
         */

        mixin: function(){
            var captures;
            if (captures = /^mixin +([-\w]+)(?: *\((.*)\))? */.exec(this.input)) {
                this.consume(captures[0].length);
                var tok = this.tok('mixin', captures[1]);
                tok.args = captures[2];
                return tok;
            }
        },

        /**
         * Conditional.
         */

        conditional: function() {
            var captures;
            if (captures = /^(if|unless|else if|else)\b([^\n]*)/.exec(this.input)) {
                this.consume(captures[0].length);
                var type = captures[1]
                var js = captures[2];
                var isIf = false;
                var isElse = false;

                switch (type) {
                    case 'if':
                        assertExpression(js)
                        js = 'if (' + js + ')';
                        isIf = true;
                        break;
                    case 'unless':
                        assertExpression(js)
                        js = 'if (!(' + js + '))';
                        isIf = true;
                        break;
                    case 'else if':
                        assertExpression(js)
                        js = 'else if (' + js + ')';
                        isIf = true;
                        isElse = true;
                        break;
                    case 'else':
                        if (js && js.trim()) {
                            throw new Error('`else` cannot have a condition, perhaps you meant `else if`');
                        }
                        js = 'else';
                        isElse = true;
                        break;
                }
                var tok = this.tok('code', js);
                tok.isElse = isElse;
                tok.isIf = isIf;
                tok.requiresBlock = true;
                return tok;
            }
        },

        /**
         * While.
         */

        "while": function() {
            var captures;
            if (captures = /^while +([^\n]+)/.exec(this.input)) {
                this.consume(captures[0].length);
                assertExpression(captures[1])
                var tok = this.tok('code', 'while (' + captures[1] + ')');
                tok.requiresBlock = true;
                return tok;
            }
        },

        /**
         * Each.
         */

        each: function() {
            var captures;
            if (captures = /^(?:- *)?(?:each|for) +([a-zA-Z_$][\w$]*)(?: *, *([a-zA-Z_$][\w$]*))? * in *([^\n]+)/.exec(this.input)) {
                this.consume(captures[0].length);
                var tok = this.tok('each', captures[1]);
                tok.key = captures[2] || '$index';
                assertExpression(captures[3])
                tok.code = captures[3];
                return tok;
            }
        },

        /**
         * Code.
         */

        code: function() {
            var captures;
            if (captures = /^(!?=|-)[ \t]*([^\n]+)/.exec(this.input)) {
                this.consume(captures[0].length);
                var flags = captures[1];
                captures[1] = captures[2];
                var tok = this.tok('code', captures[1]);
                tok.escape = flags.charAt(0) === '=';
                tok.buffer = flags.charAt(0) === '=' || flags.charAt(1) === '=';
                if (tok.buffer) assertExpression(captures[1])
                return tok;
            }
        },

        /**
         * Attributes.
         */

        attrs: function() {
            if ('(' == this.input.charAt(0)) {
                var index = this.bracketExpression().end
                    , str = this.input.substr(1, index-1)
                    , tok = this.tok('attrs');

                assertNestingCorrect(str);

                var quote = '';
                var interpolate = function (attr) {
                    return attr.replace(/(\\)?#\{(.+)/g, function(_, escape, expr){
                        if (escape) return _;
                        try {
                            var range = characterParser.parseMax(expr);
                            if (expr[range.end] !== '}') return _.substr(0, 2) + interpolate(_.substr(2));
                            assertExpression(range.src)
                            return quote + " + (" + range.src + ") + " + quote + interpolate(expr.substr(range.end + 1));
                        } catch (ex) {
                            return _.substr(0, 2) + interpolate(_.substr(2));
                        }
                    });
                }

                this.consume(index + 1);
                tok.attrs = [];

                var escapedAttr = true
                var key = '';
                var val = '';
                var interpolatable = '';
                var state = characterParser.defaultState();
                var loc = 'key';
                var isEndOfAttribute = function (i) {
                    if (key.trim() === '') return false;
                    if (i === str.length) return true;
                    if (loc === 'key') {
                        if (str[i] === ' ' || str[i] === '\n') {
                            for (var x = i; x < str.length; x++) {
                                if (str[x] != ' ' && str[x] != '\n') {
                                    if (str[x] === '=' || str[x] === '!' || str[x] === ',') return false;
                                    else return true;
                                }
                            }
                        }
                        return str[i] === ','
                    } else if (loc === 'value' && !state.isNesting()) {
                        try {
                            assertExpression(val);
                            if (str[i] === ' ' || str[i] === '\n') {
                                for (var x = i; x < str.length; x++) {
                                    if (str[x] != ' ' && str[x] != '\n') {
                                        if (characterParser.isPunctuator(str[x]) && str[x] != '"' && str[x] != "'") return false;
                                        else return true;
                                    }
                                }
                            }
                            return str[i] === ',';
                        } catch (ex) {
                            return false;
                        }
                    }
                }

                this.lineno += str.split("\n").length - 1;

                for (var i = 0; i <= str.length; i++) {
                    if (isEndOfAttribute(i)) {
                        val = val.trim();
                        if (val) assertExpression(val)
                        key = key.trim();
                        key = key.replace(/^['"]|['"]$/g, '');
                        tok.attrs.push({
                            name: key,
                            val: '' == val ? true : val,
                            escaped: escapedAttr
                        });
                        key = val = '';
                        loc = 'key';
                        escapedAttr = false;
                    } else {
                        switch (loc) {
                            case 'key-char':
                                if (str[i] === quote) {
                                    loc = 'key';
                                    if (i + 1 < str.length && [' ', ',', '!', '=', '\n'].indexOf(str[i + 1]) === -1)
                                        throw new Error('Unexpected character ' + str[i + 1] + ' expected ` `, `\\n`, `,`, `!` or `=`');
                                } else {
                                    key += str[i];
                                }
                                break;
                            case 'key':
                                if (key === '' && (str[i] === '"' || str[i] === "'")) {
                                    loc = 'key-char';
                                    quote = str[i];
                                } else if (str[i] === '!' || str[i] === '=') {
                                    escapedAttr = str[i] !== '!';
                                    if (str[i] === '!') i++;
                                    if (str[i] !== '=') throw new Error('Unexpected character ' + str[i] + ' expected `=`');
                                    loc = 'value';
                                    state = characterParser.defaultState();
                                } else {
                                    key += str[i]
                                }
                                break;
                            case 'value':
                                state = characterParser.parseChar(str[i], state);
                                if (state.isString()) {
                                    loc = 'string';
                                    quote = str[i];
                                    interpolatable = str[i];
                                } else {
                                    val += str[i];
                                }
                                break;
                            case 'string':
                                state = characterParser.parseChar(str[i], state);
                                interpolatable += str[i];
                                if (!state.isString()) {
                                    loc = 'value';
                                    val += interpolate(interpolatable);
                                }
                                break;
                        }
                    }
                }

                if ('/' == this.input.charAt(0)) {
                    this.consume(1);
                    tok.selfClosing = true;
                }

                return tok;
            }
        },

        /**
         * &attributes block
         */
        attributesBlock: function () {
            var captures;
            if (/^&attributes\b/.test(this.input)) {
                this.consume(11);
                var args = this.bracketExpression();
                this.consume(args.end + 1);
                return this.tok('&attributes', args.src);
            }
        },

        /**
         * Indent | Outdent | Newline.
         */

        indent: function() {
            var captures, re;

            // established regexp
            if (this.indentRe) {
                captures = this.indentRe.exec(this.input);
                // determine regexp
            } else {
                // tabs
                re = /^\n(\t*) */;
                captures = re.exec(this.input);

                // spaces
                if (captures && !captures[1].length) {
                    re = /^\n( *)/;
                    captures = re.exec(this.input);
                }

                // established
                if (captures && captures[1].length) this.indentRe = re;
            }

            if (captures) {
                var tok
                    , indents = captures[1].length;

                ++this.lineno;
                this.consume(indents + 1);

                if (' ' == this.input[0] || '\t' == this.input[0]) {
                    throw new Error('Invalid indentation, you can use tabs or spaces but not both');
                }

                // blank line
                if ('\n' == this.input[0]) {
                    this.pipeless = false;
                    return this.tok('newline');
                }

                // outdent
                if (this.indentStack.length && indents < this.indentStack[0]) {
                    while (this.indentStack.length && this.indentStack[0] > indents) {
                        this.stash.push(this.tok('outdent'));
                        this.indentStack.shift();
                    }
                    tok = this.stash.pop();
                    // indent
                } else if (indents && indents != this.indentStack[0]) {
                    this.indentStack.unshift(indents);
                    tok = this.tok('indent', indents);
                    // newline
                } else {
                    tok = this.tok('newline');
                }

                this.pipeless = false;
                return tok;
            }
        },

        /**
         * Pipe-less text consumed only when
         * pipeless is true;
         */

        pipelessText: function() {
            if (!this.pipeless) return;
            var captures, re;

            // established regexp
            if (this.indentRe) {
                captures = this.indentRe.exec(this.input);
                // determine regexp
            } else {
                // tabs
                re = /^\n(\t*) */;
                captures = re.exec(this.input);

                // spaces
                if (captures && !captures[1].length) {
                    re = /^\n( *)/;
                    captures = re.exec(this.input);
                }

                // established
                if (captures && captures[1].length) this.indentRe = re;
            }

            var indents = captures && captures[1].length;
            if (indents && (this.indentStack.length === 0 || indents > this.indentStack[0])) {
                var indent = captures[1];
                var line;
                var tokens = [];
                var isMatch;
                do {
                    // text has `\n` as a prefix
                    var i = this.input.substr(1).indexOf('\n');
                    if (-1 == i) i = this.input.length - 1;
                    var str = this.input.substr(1, i);
                    isMatch = str.substr(0, indent.length) === indent || !str.trim();
                    if (isMatch) {
                        // consume test along with `\n` prefix if match
                        this.consume(str.length + 1);
                        tokens.push(str.substr(indent.length));
                    }
                } while(this.input.length && isMatch);
                while (this.input.length === 0 && tokens[tokens.length - 1] === '') tokens.pop();
                return this.tok('pipeless-text', tokens);
            }
        },

        /**
         * ':'
         */

        colon: function() {
            var good = /^: +/.test(this.input);
            var res = this.scan(/^: */, ':');
            if (res && !good) {
                console.warn('Warning: space required after `:` on line ' + this.lineno +
                ' of jade file "' + this.filename + '"');
            }
            return res;
        },

        fail: function () {
            throw new Error('unexpected text ' + this.input.substr(0, 5));
        },

        /**
         * Return the next token object, or those
         * previously stashed by lookahead.
         *
         * @return {Object}
         * @api private
         */

        advance: function(){
            return this.stashed()
                || this.next();
        },

        /**
         * Return the next token object.
         *
         * @return {Object}
         * @api private
         */

        next: function() {
            return this.deferred()
                || this.blank()
                || this.eos()
                || this.pipelessText()
                || this.yield()
                || this.doctype()
                || this.interpolation()
                || this["case"]()
                || this.when()
                || this["default"]()
                || this["extends"]()
                || this.append()
                || this.prepend()
                || this.block()
                || this.mixinBlock()
                || this.include()
                || this.includeFiltered()
                || this.mixin()
                || this.call()
                || this.conditional()
                || this.each()
                || this["while"]()
                || this.tag()
                || this.filter()
                || this.code()
                || this.id()
                || this.className()
                || this.attrs()
                || this.attributesBlock()
                || this.indent()
                || this.text()
                || this.comment()
                || this.colon()
                || this.dot()
                || this.textFail()
                || this.fail();
        }
    };

},{"./utils":25,"character-parser":29}],7:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a `Attrs` node.
     *
     * @api public
     */

    var Attrs = module.exports = function Attrs() {
        this.attributeNames = [];
        this.attrs = [];
        this.attributeBlocks = [];
    };

// Inherit from `Node`.
    Attrs.prototype = Object.create(Node.prototype);
    Attrs.prototype.constructor = Attrs;

    Attrs.prototype.type = 'Attrs';

    /**
     * Set attribute `name` to `val`, keep in mind these become
     * part of a raw js object literal, so to quote a value you must
     * '"quote me"', otherwise or example 'user.name' is literal JavaScript.
     *
     * @param {String} name
     * @param {String} val
     * @param {Boolean} escaped
     * @return {Tag} for chaining
     * @api public
     */

    Attrs.prototype.setAttribute = function(name, val, escaped){
        if (name !== 'class' && this.attributeNames.indexOf(name) !== -1) {
            throw new Error('Duplicate attribute "' + name + '" is not allowed.');
        }
        this.attributeNames.push(name);
        this.attrs.push({ name: name, val: val, escaped: escaped });
        return this;
    };

    /**
     * Remove attribute `name` when present.
     *
     * @param {String} name
     * @api public
     */

    Attrs.prototype.removeAttribute = function(name){
        var err = new Error('attrs.removeAttribute is deprecated and will be removed in v2.0.0');
        console.warn(err.stack);

        for (var i = 0, len = this.attrs.length; i < len; ++i) {
            if (this.attrs[i] && this.attrs[i].name == name) {
                delete this.attrs[i];
            }
        }
    };

    /**
     * Get attribute value by `name`.
     *
     * @param {String} name
     * @return {String}
     * @api public
     */

    Attrs.prototype.getAttribute = function(name){
        var err = new Error('attrs.getAttribute is deprecated and will be removed in v2.0.0');
        console.warn(err.stack);

        for (var i = 0, len = this.attrs.length; i < len; ++i) {
            if (this.attrs[i] && this.attrs[i].name == name) {
                return this.attrs[i].val;
            }
        }
    };

    Attrs.prototype.addAttributes = function (src) {
        this.attributeBlocks.push(src);
    };

},{"./node":20}],8:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a `BlockComment` with the given `block`.
     *
     * @param {String} val
     * @param {Block} block
     * @param {Boolean} buffer
     * @api public
     */

    var BlockComment = module.exports = function BlockComment(val, block, buffer) {
        this.block = block;
        this.val = val;
        this.buffer = buffer;
    };

// Inherit from `Node`.
    BlockComment.prototype = Object.create(Node.prototype);
    BlockComment.prototype.constructor = BlockComment;

    BlockComment.prototype.type = 'BlockComment';

},{"./node":20}],9:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a new `Block` with an optional `node`.
     *
     * @param {Node} node
     * @api public
     */

    var Block = module.exports = function Block(node){
        this.nodes = [];
        if (node) this.push(node);
    };

// Inherit from `Node`.
    Block.prototype = Object.create(Node.prototype);
    Block.prototype.constructor = Block;

    Block.prototype.type = 'Block';

    /**
     * Block flag.
     */

    Block.prototype.isBlock = true;

    /**
     * Replace the nodes in `other` with the nodes
     * in `this` block.
     *
     * @param {Block} other
     * @api private
     */

    Block.prototype.replace = function(other){
        var err = new Error('block.replace is deprecated and will be removed in v2.0.0');
        console.warn(err.stack);

        other.nodes = this.nodes;
    };

    /**
     * Push the given `node`.
     *
     * @param {Node} node
     * @return {Number}
     * @api public
     */

    Block.prototype.push = function(node){
        return this.nodes.push(node);
    };

    /**
     * Check if this block is empty.
     *
     * @return {Boolean}
     * @api public
     */

    Block.prototype.isEmpty = function(){
        return 0 == this.nodes.length;
    };

    /**
     * Unshift the given `node`.
     *
     * @param {Node} node
     * @return {Number}
     * @api public
     */

    Block.prototype.unshift = function(node){
        return this.nodes.unshift(node);
    };

    /**
     * Return the "last" block, or the first `yield` node.
     *
     * @return {Block}
     * @api private
     */

    Block.prototype.includeBlock = function(){
        var ret = this
            , node;

        for (var i = 0, len = this.nodes.length; i < len; ++i) {
            node = this.nodes[i];
            if (node.yield) return node;
            else if (node.textOnly) continue;
            else if (node.includeBlock) ret = node.includeBlock();
            else if (node.block && !node.block.isEmpty()) ret = node.block.includeBlock();
            if (ret.yield) return ret;
        }

        return ret;
    };

    /**
     * Return a clone of this block.
     *
     * @return {Block}
     * @api private
     */

    Block.prototype.clone = function(){
        var err = new Error('block.clone is deprecated and will be removed in v2.0.0');
        console.warn(err.stack);

        var clone = new Block;
        for (var i = 0, len = this.nodes.length; i < len; ++i) {
            clone.push(this.nodes[i].clone());
        }
        return clone;
    };

},{"./node":20}],10:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a new `Case` with `expr`.
     *
     * @param {String} expr
     * @api public
     */

    var Case = exports = module.exports = function Case(expr, block){
        this.expr = expr;
        this.block = block;
    };

// Inherit from `Node`.
    Case.prototype = Object.create(Node.prototype);
    Case.prototype.constructor = Case;

    Case.prototype.type = 'Case';

    var When = exports.When = function When(expr, block){
        this.expr = expr;
        this.block = block;
        this.debug = false;
    };

// Inherit from `Node`.
    When.prototype = Object.create(Node.prototype);
    When.prototype.constructor = When;

    When.prototype.type = 'When';

},{"./node":20}],11:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a `Code` node with the given code `val`.
     * Code may also be optionally buffered and escaped.
     *
     * @param {String} val
     * @param {Boolean} buffer
     * @param {Boolean} escape
     * @api public
     */

    var Code = module.exports = function Code(val, buffer, escape) {
        this.val = val;
        this.buffer = buffer;
        this.escape = escape;
        if (val.match(/^ *else/)) this.debug = false;
    };

// Inherit from `Node`.
    Code.prototype = Object.create(Node.prototype);
    Code.prototype.constructor = Code;

    Code.prototype.type = 'Code'; // prevent the minifiers removing this
},{"./node":20}],12:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a `Comment` with the given `val`, optionally `buffer`,
     * otherwise the comment may render in the output.
     *
     * @param {String} val
     * @param {Boolean} buffer
     * @api public
     */

    var Comment = module.exports = function Comment(val, buffer) {
        this.val = val;
        this.buffer = buffer;
    };

// Inherit from `Node`.
    Comment.prototype = Object.create(Node.prototype);
    Comment.prototype.constructor = Comment;

    Comment.prototype.type = 'Comment';

},{"./node":20}],13:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a `Doctype` with the given `val`.
     *
     * @param {String} val
     * @api public
     */

    var Doctype = module.exports = function Doctype(val) {
        this.val = val;
    };

// Inherit from `Node`.
    Doctype.prototype = Object.create(Node.prototype);
    Doctype.prototype.constructor = Doctype;

    Doctype.prototype.type = 'Doctype';

},{"./node":20}],14:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize an `Each` node, representing iteration
     *
     * @param {String} obj
     * @param {String} val
     * @param {String} key
     * @param {Block} block
     * @api public
     */

    var Each = module.exports = function Each(obj, val, key, block) {
        this.obj = obj;
        this.val = val;
        this.key = key;
        this.block = block;
    };

// Inherit from `Node`.
    Each.prototype = Object.create(Node.prototype);
    Each.prototype.constructor = Each;

    Each.prototype.type = 'Each';

},{"./node":20}],15:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a `Filter` node with the given
     * filter `name` and `block`.
     *
     * @param {String} name
     * @param {Block|Node} block
     * @api public
     */

    var Filter = module.exports = function Filter(name, block, attrs) {
        this.name = name;
        this.block = block;
        this.attrs = attrs;
    };

// Inherit from `Node`.
    Filter.prototype = Object.create(Node.prototype);
    Filter.prototype.constructor = Filter;

    Filter.prototype.type = 'Filter';

},{"./node":20}],16:[function(require,module,exports){
    'use strict';

    exports.Node = require('./node');
    exports.Tag = require('./tag');
    exports.Code = require('./code');
    exports.Each = require('./each');
    exports.Case = require('./case');
    exports.Text = require('./text');
    exports.Block = require('./block');
    exports.MixinBlock = require('./mixin-block');
    exports.Mixin = require('./mixin');
    exports.Filter = require('./filter');
    exports.Comment = require('./comment');
    exports.Literal = require('./literal');
    exports.BlockComment = require('./block-comment');
    exports.Doctype = require('./doctype');

},{"./block":9,"./block-comment":8,"./case":10,"./code":11,"./comment":12,"./doctype":13,"./each":14,"./filter":15,"./literal":17,"./mixin":19,"./mixin-block":18,"./node":20,"./tag":21,"./text":22}],17:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a `Literal` node with the given `str.
     *
     * @param {String} str
     * @api public
     */

    var Literal = module.exports = function Literal(str) {
        this.str = str;
    };

// Inherit from `Node`.
    Literal.prototype = Object.create(Node.prototype);
    Literal.prototype.constructor = Literal;

    Literal.prototype.type = 'Literal';

},{"./node":20}],18:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a new `Block` with an optional `node`.
     *
     * @param {Node} node
     * @api public
     */

    var MixinBlock = module.exports = function MixinBlock(){};

// Inherit from `Node`.
    MixinBlock.prototype = Object.create(Node.prototype);
    MixinBlock.prototype.constructor = MixinBlock;

    MixinBlock.prototype.type = 'MixinBlock';

},{"./node":20}],19:[function(require,module,exports){
    'use strict';

    var Attrs = require('./attrs');

    /**
     * Initialize a new `Mixin` with `name` and `block`.
     *
     * @param {String} name
     * @param {String} args
     * @param {Block} block
     * @api public
     */

    var Mixin = module.exports = function Mixin(name, args, block, call){
        Attrs.call(this);
        this.name = name;
        this.args = args;
        this.block = block;
        this.call = call;
    };

// Inherit from `Attrs`.
    Mixin.prototype = Object.create(Attrs.prototype);
    Mixin.prototype.constructor = Mixin;

    Mixin.prototype.type = 'Mixin';

},{"./attrs":7}],20:[function(require,module,exports){
    'use strict';

    var Node = module.exports = function Node(){};

    /**
     * Clone this node (return itself)
     *
     * @return {Node}
     * @api private
     */

    Node.prototype.clone = function(){
        var err = new Error('node.clone is deprecated and will be removed in v2.0.0');
        console.warn(err.stack);
        return this;
    };

    Node.prototype.type = '';

},{}],21:[function(require,module,exports){
    'use strict';

    var Attrs = require('./attrs');
    var Block = require('./block');
    var inlineTags = require('../inline-tags');

    /**
     * Initialize a `Tag` node with the given tag `name` and optional `block`.
     *
     * @param {String} name
     * @param {Block} block
     * @api public
     */

    var Tag = module.exports = function Tag(name, block) {
        Attrs.call(this);
        this.name = name;
        this.block = block || new Block;
    };

// Inherit from `Attrs`.
    Tag.prototype = Object.create(Attrs.prototype);
    Tag.prototype.constructor = Tag;

    Tag.prototype.type = 'Tag';

    /**
     * Clone this tag.
     *
     * @return {Tag}
     * @api private
     */

    Tag.prototype.clone = function(){
        var err = new Error('tag.clone is deprecated and will be removed in v2.0.0');
        console.warn(err.stack);

        var clone = new Tag(this.name, this.block.clone());
        clone.line = this.line;
        clone.attrs = this.attrs;
        clone.textOnly = this.textOnly;
        return clone;
    };

    /**
     * Check if this tag is an inline tag.
     *
     * @return {Boolean}
     * @api private
     */

    Tag.prototype.isInline = function(){
        return ~inlineTags.indexOf(this.name);
    };

    /**
     * Check if this tag's contents can be inlined.  Used for pretty printing.
     *
     * @return {Boolean}
     * @api private
     */

    Tag.prototype.canInline = function(){
        var nodes = this.block.nodes;

        function isInline(node){
            // Recurse if the node is a block
            if (node.isBlock) return node.nodes.every(isInline);
            return node.isText || (node.isInline && node.isInline());
        }

        // Empty tag
        if (!nodes.length) return true;

        // Text-only or inline-only tag
        if (1 == nodes.length) return isInline(nodes[0]);

        // Multi-line inline-only tag
        if (this.block.nodes.every(isInline)) {
            for (var i = 1, len = nodes.length; i < len; ++i) {
                if (nodes[i-1].isText && nodes[i].isText)
                    return false;
            }
            return true;
        }

        // Mixed tag
        return false;
    };

},{"../inline-tags":5,"./attrs":7,"./block":9}],22:[function(require,module,exports){
    'use strict';

    var Node = require('./node');

    /**
     * Initialize a `Text` node with optional `line`.
     *
     * @param {String} line
     * @api public
     */

    var Text = module.exports = function Text(line) {
        this.val = line;
    };

// Inherit from `Node`.
    Text.prototype = Object.create(Node.prototype);
    Text.prototype.constructor = Text;

    Text.prototype.type = 'Text';

    /**
     * Flag as text.
     */

    Text.prototype.isText = true;
},{"./node":20}],23:[function(require,module,exports){
    'use strict';

    var Lexer = require('./lexer');
    var nodes = require('./nodes');
    var utils = require('./utils');
    var filters = require('./filters');
    var path = require('path');
    var constantinople = require('constantinople');
    var parseJSExpression = require('character-parser').parseMax;
    var extname = path.extname;

    /**
     * Initialize `Parser` with the given input `str` and `filename`.
     *
     * @param {String} str
     * @param {String} filename
     * @param {Object} options
     * @api public
     */

    var Parser = exports = module.exports = function Parser(str, filename, options){
        //Strip any UTF-8 BOM off of the start of `str`, if it exists.
        this.input = str.replace(/^\uFEFF/, '');
        this.lexer = new Lexer(this.input, filename);
        this.filename = filename;
        this.blocks = {};
        this.mixins = {};
        this.options = options;
        this.contexts = [this];
        this.inMixin = 0;
        this.dependencies = [];
        this.inBlock = 0;
    };

    /**
     * Parser prototype.
     */

    Parser.prototype = {

        /**
         * Save original constructor
         */

        constructor: Parser,

        /**
         * Push `parser` onto the context stack,
         * or pop and return a `Parser`.
         */

        context: function(parser){
            if (parser) {
                this.contexts.push(parser);
            } else {
                return this.contexts.pop();
            }
        },

        /**
         * Return the next token object.
         *
         * @return {Object}
         * @api private
         */

        advance: function(){
            return this.lexer.advance();
        },

        /**
         * Single token lookahead.
         *
         * @return {Object}
         * @api private
         */

        peek: function() {
            return this.lookahead(1);
        },

        /**
         * Return lexer lineno.
         *
         * @return {Number}
         * @api private
         */

        line: function() {
            return this.lexer.lineno;
        },

        /**
         * `n` token lookahead.
         *
         * @param {Number} n
         * @return {Object}
         * @api private
         */

        lookahead: function(n){
            return this.lexer.lookahead(n);
        },

        /**
         * Parse input returning a string of js for evaluation.
         *
         * @return {String}
         * @api public
         */

        parse: function(){
            var block = new nodes.Block, parser;
            block.line = 0;
            block.filename = this.filename;

            while ('eos' != this.peek().type) {
                if ('newline' == this.peek().type) {
                    this.advance();
                } else {
                    var next = this.peek();
                    var expr = this.parseExpr();
                    expr.filename = expr.filename || this.filename;
                    expr.line = next.line;
                    block.push(expr);
                }
            }

            if (parser = this.extending) {
                this.context(parser);
                var ast = parser.parse();
                this.context();

                // hoist mixins
                for (var name in this.mixins)
                    ast.unshift(this.mixins[name]);
                return ast;
            }

            if (!this.extending && !this.included && Object.keys(this.blocks).length){
                var blocks = [];
                utils.walkAST(block, function (node) {
                    if (node.type === 'Block' && node.name) {
                        blocks.push(node.name);
                    }
                });
                Object.keys(this.blocks).forEach(function (name) {
                    if (blocks.indexOf(name) === -1 && !this.blocks[name].isSubBlock) {
                        console.warn('Warning: Unexpected block "'
                        + name
                        + '" '
                        + ' on line '
                        + this.blocks[name].line
                        + ' of '
                        + (this.blocks[name].filename)
                        + '. This block is never used. This warning will be an error in v2.0.0');
                    }
                }.bind(this));
            }

            return block;
        },

        /**
         * Expect the given type, or throw an exception.
         *
         * @param {String} type
         * @api private
         */

        expect: function(type){
            if (this.peek().type === type) {
                return this.advance();
            } else {
                throw new Error('expected "' + type + '", but got "' + this.peek().type + '"');
            }
        },

        /**
         * Accept the given `type`.
         *
         * @param {String} type
         * @api private
         */

        accept: function(type){
            if (this.peek().type === type) {
                return this.advance();
            }
        },

        /**
         *   tag
         * | doctype
         * | mixin
         * | include
         * | filter
         * | comment
         * | text
         * | each
         * | code
         * | yield
         * | id
         * | class
         * | interpolation
         */

        parseExpr: function(){
            switch (this.peek().type) {
                case 'tag':
                    return this.parseTag();
                case 'mixin':
                    return this.parseMixin();
                case 'block':
                    return this.parseBlock();
                case 'mixin-block':
                    return this.parseMixinBlock();
                case 'case':
                    return this.parseCase();
                case 'extends':
                    return this.parseExtends();
                case 'include':
                    return this.parseInclude();
                case 'doctype':
                    return this.parseDoctype();
                case 'filter':
                    return this.parseFilter();
                case 'comment':
                    return this.parseComment();
                case 'text':
                    return this.parseText();
                case 'each':
                    return this.parseEach();
                case 'code':
                    return this.parseCode();
                case 'call':
                    return this.parseCall();
                case 'interpolation':
                    return this.parseInterpolation();
                case 'yield':
                    this.advance();
                    var block = new nodes.Block;
                    block.yield = true;
                    return block;
                case 'id':
                case 'class':
                    var tok = this.advance();
                    this.lexer.defer(this.lexer.tok('tag', 'div'));
                    this.lexer.defer(tok);
                    return this.parseExpr();
                default:
                    throw new Error('unexpected token "' + this.peek().type + '"');
            }
        },

        /**
         * Text
         */

        parseText: function(){
            var tok = this.expect('text');
            var tokens = this.parseInlineTagsInText(tok.val);
            if (tokens.length === 1) return tokens[0];
            var node = new nodes.Block;
            for (var i = 0; i < tokens.length; i++) {
                node.push(tokens[i]);
            };
            return node;
        },

        /**
         *   ':' expr
         * | block
         */

        parseBlockExpansion: function(){
            if (':' == this.peek().type) {
                this.advance();
                return new nodes.Block(this.parseExpr());
            } else {
                return this.block();
            }
        },

        /**
         * case
         */

        parseCase: function(){
            var val = this.expect('case').val;
            var node = new nodes.Case(val);
            node.line = this.line();

            var block = new nodes.Block;
            block.line = this.line();
            block.filename = this.filename;
            this.expect('indent');
            while ('outdent' != this.peek().type) {
                switch (this.peek().type) {
                    case 'comment':
                    case 'newline':
                        this.advance();
                        break;
                    case 'when':
                        block.push(this.parseWhen());
                        break;
                    case 'default':
                        block.push(this.parseDefault());
                        break;
                    default:
                        throw new Error('Unexpected token "' + this.peek().type
                        + '", expected "when", "default" or "newline"');
                }
            }
            this.expect('outdent');

            node.block = block;

            return node;
        },

        /**
         * when
         */

        parseWhen: function(){
            var val = this.expect('when').val;
            if (this.peek().type !== 'newline')
                return new nodes.Case.When(val, this.parseBlockExpansion());
            else
                return new nodes.Case.When(val);
        },

        /**
         * default
         */

        parseDefault: function(){
            this.expect('default');
            return new nodes.Case.When('default', this.parseBlockExpansion());
        },

        /**
         * code
         */

        parseCode: function(afterIf){
            var tok = this.expect('code');
            var node = new nodes.Code(tok.val, tok.buffer, tok.escape);
            var block;
            node.line = this.line();

            // throw an error if an else does not have an if
            if (tok.isElse && !tok.hasIf) {
                throw new Error('Unexpected else without if');
            }

            // handle block
            block = 'indent' == this.peek().type;
            if (block) {
                node.block = this.block();
            }

            // handle missing block
            if (tok.requiresBlock && !block) {
                node.block = new nodes.Block();
            }

            // mark presense of if for future elses
            if (tok.isIf && this.peek().isElse) {
                this.peek().hasIf = true;
            } else if (tok.isIf && this.peek().type === 'newline' && this.lookahead(2).isElse) {
                this.lookahead(2).hasIf = true;
            }

            return node;
        },

        /**
         * comment
         */

        parseComment: function(){
            var tok = this.expect('comment');
            var node;

            var block;
            if (block = this.parseTextBlock()) {
                node = new nodes.BlockComment(tok.val, block, tok.buffer);
            } else {
                node = new nodes.Comment(tok.val, tok.buffer);
            }

            node.line = this.line();
            return node;
        },

        /**
         * doctype
         */

        parseDoctype: function(){
            var tok = this.expect('doctype');
            var node = new nodes.Doctype(tok.val);
            node.line = this.line();
            return node;
        },

        /**
         * filter attrs? text-block
         */

        parseFilter: function(){
            var tok = this.expect('filter');
            var attrs = this.accept('attrs');
            var block;

            block = this.parseTextBlock() || new nodes.Block();

            var options = {};
            if (attrs) {
                attrs.attrs.forEach(function (attribute) {
                    options[attribute.name] = constantinople.toConstant(attribute.val);
                });
            }

            var node = new nodes.Filter(tok.val, block, options);
            node.line = this.line();
            return node;
        },

        /**
         * each block
         */

        parseEach: function(){
            var tok = this.expect('each');
            var node = new nodes.Each(tok.code, tok.val, tok.key);
            node.line = this.line();
            node.block = this.block();
            if (this.peek().type == 'code' && this.peek().val == 'else') {
                this.advance();
                node.alternative = this.block();
            }
            return node;
        },

        /**
         * Resolves a path relative to the template for use in
         * includes and extends
         *
         * @param {String}  path
         * @param {String}  purpose  Used in error messages.
         * @return {String}
         * @api private
         */

        resolvePath: function (path, purpose) {
            var p = require('path');
            var dirname = p.dirname;
            var basename = p.basename;
            var join = p.join;

            if (path[0] !== '/' && !this.filename)
                throw new Error('the "filename" option is required to use "' + purpose + '" with "relative" paths');

            if (path[0] === '/' && !this.options.basedir)
                throw new Error('the "basedir" option is required to use "' + purpose + '" with "absolute" paths');

            path = join(path[0] === '/' ? this.options.basedir : dirname(this.filename), path);

            if (basename(path).indexOf('.') === -1) path += '.jade';

            return path;
        },

        /**
         * 'extends' name
         */

        parseExtends: function(){
            var fs = require('fs');

            var path = this.resolvePath(this.expect('extends').val.trim(), 'extends');
            if ('.jade' != path.substr(-5)) path += '.jade';

            this.dependencies.push(path);
            var str = fs.readFileSync(path, 'utf8');
            var parser = new this.constructor(str, path, this.options);
            parser.dependencies = this.dependencies;

            parser.blocks = this.blocks;
            parser.included = this.included;
            parser.contexts = this.contexts;
            this.extending = parser;

            // TODO: null node
            return new nodes.Literal('');
        },

        /**
         * 'block' name block
         */

        parseBlock: function(){
            var block = this.expect('block');
            var mode = block.mode;
            var name = block.val.trim();

            var line = block.line;

            this.inBlock++;
            block = 'indent' == this.peek().type
                ? this.block()
                : new nodes.Block(new nodes.Literal(''));
            this.inBlock--;
            block.name = name;
            block.line = line;

            var prev = this.blocks[name] || {prepended: [], appended: []}
            if (prev.mode === 'replace') return this.blocks[name] = prev;

            var allNodes = prev.prepended.concat(block.nodes).concat(prev.appended);

            switch (mode) {
                case 'append':
                    prev.appended = prev.parser === this ?
                        prev.appended.concat(block.nodes) :
                        block.nodes.concat(prev.appended);
                    break;
                case 'prepend':
                    prev.prepended = prev.parser === this ?
                        block.nodes.concat(prev.prepended) :
                        prev.prepended.concat(block.nodes);
                    break;
            }
            block.nodes = allNodes;
            block.appended = prev.appended;
            block.prepended = prev.prepended;
            block.mode = mode;
            block.parser = this;

            block.isSubBlock = this.inBlock > 0;

            return this.blocks[name] = block;
        },

        parseMixinBlock: function () {
            var block = this.expect('mixin-block');
            if (!this.inMixin) {
                throw new Error('Anonymous blocks are not allowed unless they are part of a mixin.');
            }
            return new nodes.MixinBlock();
        },

        /**
         * include block?
         */

        parseInclude: function(){
            var fs = require('fs');
            var tok = this.expect('include');

            var path = this.resolvePath(tok.val.trim(), 'include');
            this.dependencies.push(path);
            // has-filter
            if (tok.filter) {
                var str = fs.readFileSync(path, 'utf8').replace(/\r/g, '');
                var options = {filename: path};
                if (tok.attrs) {
                    tok.attrs.attrs.forEach(function (attribute) {
                        options[attribute.name] = constantinople.toConstant(attribute.val);
                    });
                }
                str = filters(tok.filter, str, options);
                return new nodes.Literal(str);
            }

            // non-jade
            if ('.jade' != path.substr(-5)) {
                var str = fs.readFileSync(path, 'utf8').replace(/\r/g, '');
                return new nodes.Literal(str);
            }

            var str = fs.readFileSync(path, 'utf8');
            var parser = new this.constructor(str, path, this.options);
            parser.dependencies = this.dependencies;

            parser.blocks = utils.merge({}, this.blocks);
            parser.included = true;

            parser.mixins = this.mixins;

            this.context(parser);
            var ast = parser.parse();
            this.context();
            ast.filename = path;

            if ('indent' == this.peek().type) {
                ast.includeBlock().push(this.block());
            }

            return ast;
        },

        /**
         * call ident block
         */

        parseCall: function(){
            var tok = this.expect('call');
            var name = tok.val;
            var args = tok.args;
            var mixin = new nodes.Mixin(name, args, new nodes.Block, true);

            this.tag(mixin);
            if (mixin.code) {
                mixin.block.push(mixin.code);
                mixin.code = null;
            }
            if (mixin.block.isEmpty()) mixin.block = null;
            return mixin;
        },

        /**
         * mixin block
         */

        parseMixin: function(){
            var tok = this.expect('mixin');
            var name = tok.val;
            var args = tok.args;
            var mixin;

            // definition
            if ('indent' == this.peek().type) {
                this.inMixin++;
                mixin = new nodes.Mixin(name, args, this.block(), false);
                this.mixins[name] = mixin;
                this.inMixin--;
                return mixin;
                // call
            } else {
                return new nodes.Mixin(name, args, null, true);
            }
        },

        parseInlineTagsInText: function (str) {
            var line = this.line();

            var match = /(\\)?#\[((?:.|\n)*)$/.exec(str);
            if (match) {
                if (match[1]) { // escape
                    var text = new nodes.Text(str.substr(0, match.index) + '#[');
                    text.line = line;
                    var rest = this.parseInlineTagsInText(match[2]);
                    if (rest[0].type === 'Text') {
                        text.val += rest[0].val;
                        rest.shift();
                    }
                    return [text].concat(rest);
                } else {
                    var text = new nodes.Text(str.substr(0, match.index));
                    text.line = line;
                    var buffer = [text];
                    var rest = match[2];
                    var range = parseJSExpression(rest);
                    var inner = new Parser(range.src, this.filename, this.options);
                    buffer.push(inner.parse());
                    return buffer.concat(this.parseInlineTagsInText(rest.substr(range.end + 1)));
                }
            } else {
                var text = new nodes.Text(str);
                text.line = line;
                return [text];
            }
        },

        /**
         * indent (text | newline)* outdent
         */

        parseTextBlock: function(){
            var block = new nodes.Block;
            block.line = this.line();
            var body = this.peek();
            if (body.type !== 'pipeless-text') return;
            this.advance();
            block.nodes = body.val.reduce(function (accumulator, text) {
                return accumulator.concat(this.parseInlineTagsInText(text));
            }.bind(this), []);
            return block;
        },

        /**
         * indent expr* outdent
         */

        block: function(){
            var block = new nodes.Block;
            block.line = this.line();
            block.filename = this.filename;
            this.expect('indent');
            while ('outdent' != this.peek().type) {
                if ('newline' == this.peek().type) {
                    this.advance();
                } else {
                    var expr = this.parseExpr();
                    expr.filename = this.filename;
                    block.push(expr);
                }
            }
            this.expect('outdent');
            return block;
        },

        /**
         * interpolation (attrs | class | id)* (text | code | ':')? newline* block?
         */

        parseInterpolation: function(){
            var tok = this.advance();
            var tag = new nodes.Tag(tok.val);
            tag.buffer = true;
            return this.tag(tag);
        },

        /**
         * tag (attrs | class | id)* (text | code | ':')? newline* block?
         */

        parseTag: function(){
            var tok = this.advance();
            var tag = new nodes.Tag(tok.val);

            tag.selfClosing = tok.selfClosing;

            return this.tag(tag);
        },

        /**
         * Parse tag.
         */

        tag: function(tag){
            tag.line = this.line();

            var seenAttrs = false;
            // (attrs | class | id)*
            out:
                while (true) {
                    switch (this.peek().type) {
                        case 'id':
                        case 'class':
                            var tok = this.advance();
                            tag.setAttribute(tok.type, "'" + tok.val + "'");
                            continue;
                        case 'attrs':
                            if (seenAttrs) {
                                console.warn(this.filename + ', line ' + this.peek().line + ':\nYou should not have jade tags with multiple attributes.');
                            }
                            seenAttrs = true;
                            var tok = this.advance();
                            var attrs = tok.attrs;

                            if (tok.selfClosing) tag.selfClosing = true;

                            for (var i = 0; i < attrs.length; i++) {
                                tag.setAttribute(attrs[i].name, attrs[i].val, attrs[i].escaped);
                            }
                            continue;
                        case '&attributes':
                            var tok = this.advance();
                            tag.addAttributes(tok.val);
                            break;
                        default:
                            break out;
                    }
                }

            // check immediate '.'
            if ('dot' == this.peek().type) {
                tag.textOnly = true;
                this.advance();
            }

            // (text | code | ':')?
            switch (this.peek().type) {
                case 'text':
                    tag.block.push(this.parseText());
                    break;
                case 'code':
                    tag.code = this.parseCode();
                    break;
                case ':':
                    this.advance();
                    tag.block = new nodes.Block;
                    tag.block.push(this.parseExpr());
                    break;
                case 'newline':
                case 'indent':
                case 'outdent':
                case 'eos':
                case 'pipeless-text':
                    break;
                default:
                    throw new Error('Unexpected token `' + this.peek().type + '` expected `text`, `code`, `:`, `newline` or `eos`')
            }

            // newline*
            while ('newline' == this.peek().type) this.advance();

            // block?
            if (tag.textOnly) {
                tag.block = this.parseTextBlock() || new nodes.Block();
            } else if ('indent' == this.peek().type) {
                var block = this.block();
                for (var i = 0, len = block.nodes.length; i < len; ++i) {
                    tag.block.push(block.nodes[i]);
                }
            }

            return tag;
        }
    };

},{"./filters":3,"./lexer":6,"./nodes":16,"./utils":25,"character-parser":29,"constantinople":30,"fs":26,"path":27}],24:[function(require,module,exports){
    'use strict';

    /**
     * Merge two attribute objects giving precedence
     * to values in object `b`. Classes are special-cased
     * allowing for arrays and merging/joining appropriately
     * resulting in a string.
     *
     * @param {Object} a
     * @param {Object} b
     * @return {Object} a
     * @api private
     */

    exports.merge = function merge(a, b) {
        if (arguments.length === 1) {
            var attrs = a[0];
            for (var i = 1; i < a.length; i++) {
                attrs = merge(attrs, a[i]);
            }
            return attrs;
        }
        var ac = a['class'];
        var bc = b['class'];

        if (ac || bc) {
            ac = ac || [];
            bc = bc || [];
            if (!Array.isArray(ac)) ac = [ac];
            if (!Array.isArray(bc)) bc = [bc];
            a['class'] = ac.concat(bc).filter(nulls);
        }

        for (var key in b) {
            if (key != 'class') {
                a[key] = b[key];
            }
        }

        return a;
    };

    /**
     * Filter null `val`s.
     *
     * @param {*} val
     * @return {Boolean}
     * @api private
     */

    function nulls(val) {
        return val != null && val !== '';
    }

    /**
     * join array as classes.
     *
     * @param {*} val
     * @return {String}
     */
    exports.joinClasses = joinClasses;
    function joinClasses(val) {
        return (Array.isArray(val) ? val.map(joinClasses) :
            (val && typeof val === 'object') ? Object.keys(val).filter(function (key) { return val[key]; }) :
                [val]).filter(nulls).join(' ');
    }

    /**
     * Render the given classes.
     *
     * @param {Array} classes
     * @param {Array.<Boolean>} escaped
     * @return {String}
     */
    exports.cls = function cls(classes, escaped) {
        var buf = [];
        for (var i = 0; i < classes.length; i++) {
            if (escaped && escaped[i]) {
                buf.push(exports.escape(joinClasses([classes[i]])));
            } else {
                buf.push(joinClasses(classes[i]));
            }
        }
        var text = joinClasses(buf);
        if (text.length) {
            return ' class="' + text + '"';
        } else {
            return '';
        }
    };


    exports.style = function (val) {
        if (val && typeof val === 'object') {
            return Object.keys(val).map(function (style) {
                return style + ':' + val[style];
            }).join(';');
        } else {
            return val;
        }
    };
    /**
     * Render the given attribute.
     *
     * @param {String} key
     * @param {String} val
     * @param {Boolean} escaped
     * @param {Boolean} terse
     * @return {String}
     */
    exports.attr = function attr(key, val, escaped, terse) {
        if (key === 'style') {
            val = exports.style(val);
        }
        if ('boolean' == typeof val || null == val) {
            if (val) {
                return ' ' + (terse ? key : key + '="' + key + '"');
            } else {
                return '';
            }
        } else if (0 == key.indexOf('data') && 'string' != typeof val) {
            if (JSON.stringify(val).indexOf('&') !== -1) {
                console.warn('Since Jade 2.0.0, ampersands (`&`) in data attributes ' +
                'will be escaped to `&amp;`');
            };
            if (val && typeof val.toISOString === 'function') {
                console.warn('Jade will eliminate the double quotes around dates in ' +
                'ISO form after 2.0.0');
            }
            return ' ' + key + "='" + JSON.stringify(val).replace(/'/g, '&apos;') + "'";
        } else if (escaped) {
            if (val && typeof val.toISOString === 'function') {
                console.warn('Jade will stringify dates in ISO form after 2.0.0');
            }
            return ' ' + key + '="' + exports.escape(val) + '"';
        } else {
            if (val && typeof val.toISOString === 'function') {
                console.warn('Jade will stringify dates in ISO form after 2.0.0');
            }
            return ' ' + key + '="' + val + '"';
        }
    };

    /**
     * Render the given attributes object.
     *
     * @param {Object} obj
     * @param {Object} escaped
     * @return {String}
     */
    exports.attrs = function attrs(obj, terse){
        var buf = [];

        var keys = Object.keys(obj);

        if (keys.length) {
            for (var i = 0; i < keys.length; ++i) {
                var key = keys[i]
                    , val = obj[key];

                if ('class' == key) {
                    if (val = joinClasses(val)) {
                        buf.push(' ' + key + '="' + val + '"');
                    }
                } else {
                    buf.push(exports.attr(key, val, false, terse));
                }
            }
        }

        return buf.join('');
    };

    /**
     * Escape the given string of `html`.
     *
     * @param {String} html
     * @return {String}
     * @api private
     */

    exports.escape = function escape(html){
        var result = String(html)
            .replace(/&/g, '&amp;')
            .replace(/</g, '&lt;')
            .replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;');
        if (result === '' + html) return html;
        else return result;
    };

    /**
     * Re-throw the given `err` in context to the
     * the jade in `filename` at the given `lineno`.
     *
     * @param {Error} err
     * @param {String} filename
     * @param {String} lineno
     * @api private
     */

    exports.rethrow = function rethrow(err, filename, lineno, str){
        if (!(err instanceof Error)) throw err;
        if ((typeof window != 'undefined' || !filename) && !str) {
            err.message += ' on line ' + lineno;
            throw err;
        }
        try {
            str = str || require('fs').readFileSync(filename, 'utf8')
        } catch (ex) {
            rethrow(err, null, lineno)
        }
        var context = 3
            , lines = str.split('\n')
            , start = Math.max(lineno - context, 0)
            , end = Math.min(lines.length, lineno + context);

        // Error context
        var context = lines.slice(start, end).map(function(line, i){
            var curr = i + start + 1;
            return (curr == lineno ? '  > ' : '    ')
                + curr
                + '| '
                + line;
        }).join('\n');

        // Alter exception message
        err.path = filename;
        err.message = (filename || 'Jade') + ':' + lineno
        + '\n' + context + '\n\n' + err.message;
        throw err;
    };

},{"fs":26}],25:[function(require,module,exports){
    'use strict';

    /**
     * Merge `b` into `a`.
     *
     * @param {Object} a
     * @param {Object} b
     * @return {Object}
     * @api public
     */

    exports.merge = function(a, b) {
        for (var key in b) a[key] = b[key];
        return a;
    };

    exports.stringify = function(str) {
        return JSON.stringify(str)
            .replace(/\u2028/g, '\\u2028')
            .replace(/\u2029/g, '\\u2029');
    };

    exports.walkAST = function walkAST(ast, before, after) {
        before && before(ast);
        switch (ast.type) {
            case 'Block':
                ast.nodes.forEach(function (node) {
                    walkAST(node, before, after);
                });
                break;
            case 'Case':
            case 'Each':
            case 'Mixin':
            case 'Tag':
            case 'When':
            case 'Code':
                ast.block && walkAST(ast.block, before, after);
                break;
            case 'Attrs':
            case 'BlockComment':
            case 'Comment':
            case 'Doctype':
            case 'Filter':
            case 'Literal':
            case 'MixinBlock':
            case 'Text':
                break;
            default:
                throw new Error('Unexpected node type ' + ast.type);
                break;
        }
        after && after(ast);
    };

},{}],26:[function(require,module,exports){

},{}],27:[function(require,module,exports){
    (function (process){
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// resolves . and .. elements in a path array with directory names there
// must be no slashes, empty elements, or device names (c:\) in the array
// (so also no leading and trailing slashes - it does not distinguish
// relative and absolute paths)
        function normalizeArray(parts, allowAboveRoot) {
            // if the path tries to go above the root, `up` ends up > 0
            var up = 0;
            for (var i = parts.length - 1; i >= 0; i--) {
                var last = parts[i];
                if (last === '.') {
                    parts.splice(i, 1);
                } else if (last === '..') {
                    parts.splice(i, 1);
                    up++;
                } else if (up) {
                    parts.splice(i, 1);
                    up--;
                }
            }

            // if the path is allowed to go above the root, restore leading ..s
            if (allowAboveRoot) {
                for (; up--; up) {
                    parts.unshift('..');
                }
            }

            return parts;
        }

// Split a filename into [root, dir, basename, ext], unix version
// 'root' is just a slash, or nothing.
        var splitPathRe =
            /^(\/?|)([\s\S]*?)((?:\.{1,2}|[^\/]+?|)(\.[^.\/]*|))(?:[\/]*)$/;
        var splitPath = function(filename) {
            return splitPathRe.exec(filename).slice(1);
        };

// path.resolve([from ...], to)
// posix version
        exports.resolve = function() {
            var resolvedPath = '',
                resolvedAbsolute = false;

            for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i--) {
                var path = (i >= 0) ? arguments[i] : process.cwd();

                // Skip empty and invalid entries
                if (typeof path !== 'string') {
                    throw new TypeError('Arguments to path.resolve must be strings');
                } else if (!path) {
                    continue;
                }

                resolvedPath = path + '/' + resolvedPath;
                resolvedAbsolute = path.charAt(0) === '/';
            }

            // At this point the path should be resolved to a full absolute path, but
            // handle relative paths to be safe (might happen when process.cwd() fails)

            // Normalize the path
            resolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {
                return !!p;
            }), !resolvedAbsolute).join('/');

            return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';
        };

// path.normalize(path)
// posix version
        exports.normalize = function(path) {
            var isAbsolute = exports.isAbsolute(path),
                trailingSlash = substr(path, -1) === '/';

            // Normalize the path
            path = normalizeArray(filter(path.split('/'), function(p) {
                return !!p;
            }), !isAbsolute).join('/');

            if (!path && !isAbsolute) {
                path = '.';
            }
            if (path && trailingSlash) {
                path += '/';
            }

            return (isAbsolute ? '/' : '') + path;
        };

// posix version
        exports.isAbsolute = function(path) {
            return path.charAt(0) === '/';
        };

// posix version
        exports.join = function() {
            var paths = Array.prototype.slice.call(arguments, 0);
            return exports.normalize(filter(paths, function(p, index) {
                if (typeof p !== 'string') {
                    throw new TypeError('Arguments to path.join must be strings');
                }
                return p;
            }).join('/'));
        };


// path.relative(from, to)
// posix version
        exports.relative = function(from, to) {
            from = exports.resolve(from).substr(1);
            to = exports.resolve(to).substr(1);

            function trim(arr) {
                var start = 0;
                for (; start < arr.length; start++) {
                    if (arr[start] !== '') break;
                }

                var end = arr.length - 1;
                for (; end >= 0; end--) {
                    if (arr[end] !== '') break;
                }

                if (start > end) return [];
                return arr.slice(start, end - start + 1);
            }

            var fromParts = trim(from.split('/'));
            var toParts = trim(to.split('/'));

            var length = Math.min(fromParts.length, toParts.length);
            var samePartsLength = length;
            for (var i = 0; i < length; i++) {
                if (fromParts[i] !== toParts[i]) {
                    samePartsLength = i;
                    break;
                }
            }

            var outputParts = [];
            for (var i = samePartsLength; i < fromParts.length; i++) {
                outputParts.push('..');
            }

            outputParts = outputParts.concat(toParts.slice(samePartsLength));

            return outputParts.join('/');
        };

        exports.sep = '/';
        exports.delimiter = ':';

        exports.dirname = function(path) {
            var result = splitPath(path),
                root = result[0],
                dir = result[1];

            if (!root && !dir) {
                // No dirname whatsoever
                return '.';
            }

            if (dir) {
                // It has a dirname, strip trailing slash
                dir = dir.substr(0, dir.length - 1);
            }

            return root + dir;
        };


        exports.basename = function(path, ext) {
            var f = splitPath(path)[2];
            // TODO: make this comparison case-insensitive on windows?
            if (ext && f.substr(-1 * ext.length) === ext) {
                f = f.substr(0, f.length - ext.length);
            }
            return f;
        };


        exports.extname = function(path) {
            return splitPath(path)[3];
        };

        function filter (xs, f) {
            if (xs.filter) return xs.filter(f);
            var res = [];
            for (var i = 0; i < xs.length; i++) {
                if (f(xs[i], i, xs)) res.push(xs[i]);
            }
            return res;
        }

// String.prototype.substr - negative index don't work in IE8
        var substr = 'ab'.substr(-1) === 'b'
                ? function (str, start, len) { return str.substr(start, len) }
                : function (str, start, len) {
                if (start < 0) start = str.length + start;
                return str.substr(start, len);
            }
            ;

    }).call(this,require('_process'))
},{"_process":28}],28:[function(require,module,exports){
// shim for using process in browser

    var process = module.exports = {};

    process.nextTick = (function () {
        var canSetImmediate = typeof window !== 'undefined'
            && window.setImmediate;
        var canMutationObserver = typeof window !== 'undefined'
            && window.MutationObserver;
        var canPost = typeof window !== 'undefined'
                && window.postMessage && window.addEventListener
            ;

        if (canSetImmediate) {
            return function (f) { return window.setImmediate(f) };
        }

        var queue = [];

        if (canMutationObserver) {
            var hiddenDiv = document.createElement("div");
            var observer = new MutationObserver(function () {
                var queueList = queue.slice();
                queue.length = 0;
                queueList.forEach(function (fn) {
                    fn();
                });
            });

            observer.observe(hiddenDiv, { attributes: true });

            return function nextTick(fn) {
                if (!queue.length) {
                    hiddenDiv.setAttribute('yes', 'no');
                }
                queue.push(fn);
            };
        }

        if (canPost) {
            window.addEventListener('message', function (ev) {
                var source = ev.source;
                if ((source === window || source === null) && ev.data === 'process-tick') {
                    ev.stopPropagation();
                    if (queue.length > 0) {
                        var fn = queue.shift();
                        fn();
                    }
                }
            }, true);

            return function nextTick(fn) {
                queue.push(fn);
                window.postMessage('process-tick', '*');
            };
        }

        return function nextTick(fn) {
            setTimeout(fn, 0);
        };
    })();

    process.title = 'browser';
    process.browser = true;
    process.env = {};
    process.argv = [];

    function noop() {}

    process.on = noop;
    process.addListener = noop;
    process.once = noop;
    process.off = noop;
    process.removeListener = noop;
    process.removeAllListeners = noop;
    process.emit = noop;

    process.binding = function (name) {
        throw new Error('process.binding is not supported');
    };

// TODO(shtylman)
    process.cwd = function () { return '/' };
    process.chdir = function (dir) {
        throw new Error('process.chdir is not supported');
    };

},{}],29:[function(require,module,exports){
    exports = (module.exports = parse);
    exports.parse = parse;
    function parse(src, state, options) {
        options = options || {};
        state = state || exports.defaultState();
        var start = options.start || 0;
        var end = options.end || src.length;
        var index = start;
        while (index < end) {
            if (state.roundDepth < 0 || state.curlyDepth < 0 || state.squareDepth < 0) {
                throw new SyntaxError('Mismatched Bracket: ' + src[index - 1]);
            }
            exports.parseChar(src[index++], state);
        }
        return state;
    }

    exports.parseMax = parseMax;
    function parseMax(src, options) {
        options = options || {};
        var start = options.start || 0;
        var index = start;
        var state = exports.defaultState();
        while (state.roundDepth >= 0 && state.curlyDepth >= 0 && state.squareDepth >= 0) {
            if (index >= src.length) {
                throw new Error('The end of the string was reached with no closing bracket found.');
            }
            exports.parseChar(src[index++], state);
        }
        var end = index - 1;
        return {
            start: start,
            end: end,
            src: src.substring(start, end)
        };
    }

    exports.parseUntil = parseUntil;
    function parseUntil(src, delimiter, options) {
        options = options || {};
        var includeLineComment = options.includeLineComment || false;
        var start = options.start || 0;
        var index = start;
        var state = exports.defaultState();
        while (state.isString() || state.regexp || state.blockComment ||
        (!includeLineComment && state.lineComment) || !startsWith(src, delimiter, index)) {
            exports.parseChar(src[index++], state);
        }
        var end = index;
        return {
            start: start,
            end: end,
            src: src.substring(start, end)
        };
    }


    exports.parseChar = parseChar;
    function parseChar(character, state) {
        if (character.length !== 1) throw new Error('Character must be a string of length 1');
        state = state || exports.defaultState();
        state.src = state.src || '';
        state.src += character;
        var wasComment = state.blockComment || state.lineComment;
        var lastChar = state.history ? state.history[0] : '';

        if (state.regexpStart) {
            if (character === '/' || character == '*') {
                state.regexp = false;
            }
            state.regexpStart = false;
        }
        if (state.lineComment) {
            if (character === '\n') {
                state.lineComment = false;
            }
        } else if (state.blockComment) {
            if (state.lastChar === '*' && character === '/') {
                state.blockComment = false;
            }
        } else if (state.singleQuote) {
            if (character === '\'' && !state.escaped) {
                state.singleQuote = false;
            } else if (character === '\\' && !state.escaped) {
                state.escaped = true;
            } else {
                state.escaped = false;
            }
        } else if (state.doubleQuote) {
            if (character === '"' && !state.escaped) {
                state.doubleQuote = false;
            } else if (character === '\\' && !state.escaped) {
                state.escaped = true;
            } else {
                state.escaped = false;
            }
        } else if (state.regexp) {
            if (character === '/' && !state.escaped) {
                state.regexp = false;
            } else if (character === '\\' && !state.escaped) {
                state.escaped = true;
            } else {
                state.escaped = false;
            }
        } else if (lastChar === '/' && character === '/') {
            state.history = state.history.substr(1);
            state.lineComment = true;
        } else if (lastChar === '/' && character === '*') {
            state.history = state.history.substr(1);
            state.blockComment = true;
        } else if (character === '/' && isRegexp(state.history)) {
            state.regexp = true;
            state.regexpStart = true;
        } else if (character === '\'') {
            state.singleQuote = true;
        } else if (character === '"') {
            state.doubleQuote = true;
        } else if (character === '(') {
            state.roundDepth++;
        } else if (character === ')') {
            state.roundDepth--;
        } else if (character === '{') {
            state.curlyDepth++;
        } else if (character === '}') {
            state.curlyDepth--;
        } else if (character === '[') {
            state.squareDepth++;
        } else if (character === ']') {
            state.squareDepth--;
        }
        if (!state.blockComment && !state.lineComment && !wasComment) state.history = character + state.history;
        state.lastChar = character; // store last character for ending block comments
        return state;
    }

    exports.defaultState = function () { return new State() };
    function State() {
        this.lineComment = false;
        this.blockComment = false;

        this.singleQuote = false;
        this.doubleQuote = false;
        this.regexp = false;

        this.escaped = false;

        this.roundDepth = 0;
        this.curlyDepth = 0;
        this.squareDepth = 0;

        this.history = ''
        this.lastChar = ''
    }
    State.prototype.isString = function () {
        return this.singleQuote || this.doubleQuote;
    }
    State.prototype.isComment = function () {
        return this.lineComment || this.blockComment;
    }
    State.prototype.isNesting = function () {
        return this.isString() || this.isComment() || this.regexp || this.roundDepth > 0 || this.curlyDepth > 0 || this.squareDepth > 0
    }

    function startsWith(str, start, i) {
        return str.substr(i || 0, start.length) === start;
    }

    exports.isPunctuator = isPunctuator
    function isPunctuator(c) {
        if (!c) return true; // the start of a string is a punctuator
        var code = c.charCodeAt(0)

        switch (code) {
            case 46:   // . dot
            case 40:   // ( open bracket
            case 41:   // ) close bracket
            case 59:   // ; semicolon
            case 44:   // , comma
            case 123:  // { open curly brace
            case 125:  // } close curly brace
            case 91:   // [
            case 93:   // ]
            case 58:   // :
            case 63:   // ?
            case 126:  // ~
            case 37:   // %
            case 38:   // &
            case 42:   // *:
            case 43:   // +
            case 45:   // -
            case 47:   // /
            case 60:   // <
            case 62:   // >
            case 94:   // ^
            case 124:  // |
            case 33:   // !
            case 61:   // =
                return true;
            default:
                return false;
        }
    }
    exports.isKeyword = isKeyword
    function isKeyword(id) {
        return (id === 'if') || (id === 'in') || (id === 'do') || (id === 'var') || (id === 'for') || (id === 'new') ||
            (id === 'try') || (id === 'let') || (id === 'this') || (id === 'else') || (id === 'case') ||
            (id === 'void') || (id === 'with') || (id === 'enum') || (id === 'while') || (id === 'break') || (id === 'catch') ||
            (id === 'throw') || (id === 'const') || (id === 'yield') || (id === 'class') || (id === 'super') ||
            (id === 'return') || (id === 'typeof') || (id === 'delete') || (id === 'switch') || (id === 'export') ||
            (id === 'import') || (id === 'default') || (id === 'finally') || (id === 'extends') || (id === 'function') ||
            (id === 'continue') || (id === 'debugger') || (id === 'package') || (id === 'private') || (id === 'interface') ||
            (id === 'instanceof') || (id === 'implements') || (id === 'protected') || (id === 'public') || (id === 'static') ||
            (id === 'yield') || (id === 'let');
    }

    function isRegexp(history) {
        //could be start of regexp or divide sign

        history = history.replace(/^\s*/, '');

        //unless its an `if`, `while`, `for` or `with` it's a divide, so we assume it's a divide
        if (history[0] === ')') return false;
        //unless it's a function expression, it's a regexp, so we assume it's a regexp
        if (history[0] === '}') return true;
        //any punctuation means it's a regexp
        if (isPunctuator(history[0])) return true;
        //if the last thing was a keyword then it must be a regexp (e.g. `typeof /foo/`)
        if (/^\w+\b/.test(history) && isKeyword(/^\w+\b/.exec(history)[0].split('').reverse().join(''))) return true;

        return false;
    }

},{}],30:[function(require,module,exports){
    'use strict'

    var detect = require('acorn-globals');

    var lastSRC = '(null)';
    var lastRes = true;
    var lastConstants = undefined;

    module.exports = isConstant;
    function isConstant(src, constants) {
        src = '(' + src + ')';
        if (lastSRC === src && lastConstants === constants) return lastRes;
        lastSRC = src;
        lastConstants = constants;
        try {
            isExpression(src);
            return lastRes = (detect(src).filter(function (key) {
                return !constants || !(key.name in constants);
            }).length === 0);
        } catch (ex) {
            return lastRes = false;
        }
    }
    isConstant.isConstant = isConstant;

    isConstant.toConstant = toConstant;
    function toConstant(src, constants) {
        if (!isConstant(src, constants)) throw new Error(JSON.stringify(src) + ' is not constant.');
        return Function(Object.keys(constants || {}).join(','), 'return (' + src + ')').apply(null, Object.keys(constants || {}).map(function (key) {
            return constants[key];
        }));
    }

    function isExpression(src) {
        try {
            eval('throw "STOP"; (function () { return (' + src + '); })()');
            return false;
        }
        catch (err) {
            return err === 'STOP';
        }
    }

},{"acorn-globals":31}],31:[function(require,module,exports){
    'use strict';

    var acorn = require('acorn');
    var walk = require('acorn/util/walk');

    function isScope(node) {
        return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'Program';
    }
    function isBlockScope(node) {
        return node.type === 'BlockStatement' || isScope(node);
    }

    function declaresArguments(node) {
        return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration' || node.type === 'ArrowFunction';
    }
    function declaresThis(node) {
        return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration';
    }

    module.exports = findGlobals;
    function findGlobals(source) {
        var globals = [];
        var ast = typeof source === 'string' ? acorn.parse(source, { ecmaVersion: 6, allowReturnOutsideFunction: true }) : source;
        if (!(ast && typeof ast === 'object' && ast.type === 'Program')) {
            throw new TypeError('Source must be either a string of JavaScript or an acorn AST');
        }
        var declareFunction = function (node) {
            var fn = node;
            fn.locals = fn.locals || {};
            node.params.forEach(function (node) {
                fn.locals[node.name] = true;
            });
            if (node.id) {
                fn.locals[node.id.name] = true;
            }
        }
        walk.ancestor(ast, {
            'VariableDeclaration': function (node, parents) {
                var parent = null;
                for (var i = parents.length - 1; i >= 0 && parent === null; i--) {
                    if (node.kind === 'var' ? isScope(parents[i]) : isBlockScope(parents[i])) {
                        parent = parents[i];
                    }
                }
                parent.locals = parent.locals || {};
                node.declarations.forEach(function (declaration) {
                    parent.locals[declaration.id.name] = true;
                });
            },
            'FunctionDeclaration': function (node, parents) {
                var parent = null;
                for (var i = parents.length - 2; i >= 0 && parent === null; i--) {
                    if (isScope(parents[i])) {
                        parent = parents[i];
                    }
                }
                parent.locals = parent.locals || {};
                parent.locals[node.id.name] = true;
                declareFunction(node);
            },
            'Function': declareFunction,
            'TryStatement': function (node) {
                node.handler.body.locals = node.handler.body.locals || {};
                node.handler.body.locals[node.handler.param.name] = true;
            }
        });
        walk.ancestor(ast, {
            'Identifier': function (node, parents) {
                var name = node.name;
                if (name === 'undefined') return;
                for (var i = 0; i < parents.length; i++) {
                    if (name === 'arguments' && declaresArguments(parents[i])) {
                        return;
                    }
                    if (parents[i].locals && name in parents[i].locals) {
                        return;
                    }
                }
                node.parents = parents;
                globals.push(node);
            },
            ThisExpression: function (node, parents) {
                for (var i = 0; i < parents.length; i++) {
                    if (declaresThis(parents[i])) {
                        return;
                    }
                }
                node.parents = parents;
                globals.push(node);
            }
        });
        var groupedGlobals = {};
        globals.forEach(function (node) {
            groupedGlobals[node.name] = (groupedGlobals[node.name] || []);
            groupedGlobals[node.name].push(node);
        });
        return Object.keys(groupedGlobals).sort().map(function (name) {
            return {name: name, nodes: groupedGlobals[name]};
        });
    }

},{"acorn":32,"acorn/util/walk":33}],32:[function(require,module,exports){
// Acorn is a tiny, fast JavaScript parser written in JavaScript.
//
// Acorn was written by Marijn Haverbeke and various contributors and
// released under an MIT license. The Unicode regexps (for identifiers
// and whitespace) were taken from [Esprima](http://esprima.org) by
// Ariya Hidayat.
//
// Git repositories for Acorn are available at
//
//     http://marijnhaverbeke.nl/git/acorn
//     https://github.com/marijnh/acorn.git
//
// Please use the [github bug tracker][ghbt] to report issues.
//
// [ghbt]: https://github.com/marijnh/acorn/issues
//
// This file defines the main parser interface. The library also comes
// with a [error-tolerant parser][dammit] and an
// [abstract syntax tree walker][walk], defined in other files.
//
// [dammit]: acorn_loose.js
// [walk]: util/walk.js

    (function(root, mod) {
        if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
        if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
        mod(root.acorn || (root.acorn = {})); // Plain browser env
    })(this, function(exports) {
        "use strict";

        exports.version = "0.8.0";

        // The main exported interface (under `self.acorn` when in the
        // browser) is a `parse` function that takes a code string and
        // returns an abstract syntax tree as specified by [Mozilla parser
        // API][api], with the caveat that inline XML is not recognized.
        //
        // [api]: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API

        var options, input, inputLen, sourceFile;

        exports.parse = function(inpt, opts) {
            input = String(inpt); inputLen = input.length;
            setOptions(opts);
            initTokenState();
            return parseTopLevel(options.program);
        };

        // A second optional argument can be given to further configure
        // the parser process. These options are recognized:

        var defaultOptions = exports.defaultOptions = {
            // `ecmaVersion` indicates the ECMAScript version to parse. Must
            // be either 3, or 5, or 6. This influences support for strict
            // mode, the set of reserved words, support for getters and
            // setters and other features.
            ecmaVersion: 5,
            // Turn on `strictSemicolons` to prevent the parser from doing
            // automatic semicolon insertion.
            strictSemicolons: false,
            // When `allowTrailingCommas` is false, the parser will not allow
            // trailing commas in array and object literals.
            allowTrailingCommas: true,
            // By default, reserved words are not enforced. Enable
            // `forbidReserved` to enforce them. When this option has the
            // value "everywhere", reserved words and keywords can also not be
            // used as property names.
            forbidReserved: false,
            // When enabled, a return at the top level is not considered an
            // error.
            allowReturnOutsideFunction: false,
            // When `locations` is on, `loc` properties holding objects with
            // `start` and `end` properties in `{line, column}` form (with
            // line being 1-based and column 0-based) will be attached to the
            // nodes.
            locations: false,
            // A function can be passed as `onToken` option, which will
            // cause Acorn to call that function with object in the same
            // format as tokenize() returns. Note that you are not
            // allowed to call the parser from the callback—that will
            // corrupt its internal state.
            onToken: null,
            // A function can be passed as `onComment` option, which will
            // cause Acorn to call that function with `(block, text, start,
            // end)` parameters whenever a comment is skipped. `block` is a
            // boolean indicating whether this is a block (`/* */`) comment,
            // `text` is the content of the comment, and `start` and `end` are
            // character offsets that denote the start and end of the comment.
            // When the `locations` option is on, two more parameters are
            // passed, the full `{line, column}` locations of the start and
            // end of the comments. Note that you are not allowed to call the
            // parser from the callback—that will corrupt its internal state.
            onComment: null,
            // Nodes have their start and end characters offsets recorded in
            // `start` and `end` properties (directly on the node, rather than
            // the `loc` object, which holds line/column data. To also add a
            // [semi-standardized][range] `range` property holding a `[start,
            // end]` array with the same numbers, set the `ranges` option to
            // `true`.
            //
            // [range]: https://bugzilla.mozilla.org/show_bug.cgi?id=745678
            ranges: false,
            // It is possible to parse multiple files into a single AST by
            // passing the tree produced by parsing the first file as
            // `program` option in subsequent parses. This will add the
            // toplevel forms of the parsed file to the `Program` (top) node
            // of an existing parse tree.
            program: null,
            // When `locations` is on, you can pass this to record the source
            // file in every node's `loc` object.
            sourceFile: null,
            // This value, if given, is stored in every node, whether
            // `locations` is on or off.
            directSourceFile: null
        };

        var isArray = function (obj) {
            return Object.prototype.toString.call(obj) === "[object Array]";
        };

        function setOptions(opts) {
            options = opts || {};
            for (var opt in defaultOptions) if (!has(options, opt))
                options[opt] = defaultOptions[opt];
            sourceFile = options.sourceFile || null;
            if (isArray(options.onToken)) {
                var tokens = options.onToken;
                options.onToken = function (token) {
                    tokens.push(token);
                };
            }
            if (isArray(options.onComment)) {
                var comments = options.onComment;
                options.onComment = function (block, text, start, end, startLoc, endLoc) {
                    var comment = {
                        type: block ? 'Block' : 'Line',
                        value: text,
                        start: start,
                        end: end
                    };
                    if (options.locations) {
                        comment.loc = new SourceLocation();
                        comment.loc.start = startLoc;
                        comment.loc.end = endLoc;
                    }
                    if (options.ranges)
                        comment.range = [start, end];
                    comments.push(comment);
                };
            }
            isKeyword = options.ecmaVersion >= 6 ? isEcma6Keyword : isEcma5AndLessKeyword;
        }

        // The `getLineInfo` function is mostly useful when the
        // `locations` option is off (for performance reasons) and you
        // want to find the line/column position for a given character
        // offset. `input` should be the code string that the offset refers
        // into.

        var getLineInfo = exports.getLineInfo = function(input, offset) {
            for (var line = 1, cur = 0;;) {
                lineBreak.lastIndex = cur;
                var match = lineBreak.exec(input);
                if (match && match.index < offset) {
                    ++line;
                    cur = match.index + match[0].length;
                } else break;
            }
            return {line: line, column: offset - cur};
        };

        function Token() {
            this.type = tokType;
            this.value = tokVal;
            this.start = tokStart;
            this.end = tokEnd;
            if (options.locations) {
                this.loc = new SourceLocation();
                this.loc.end = tokEndLoc;
                // TODO: remove in next major release
                this.startLoc = tokStartLoc;
                this.endLoc = tokEndLoc;
            }
            if (options.ranges)
                this.range = [tokStart, tokEnd];
        }

        exports.Token = Token;

        // Acorn is organized as a tokenizer and a recursive-descent parser.
        // The `tokenize` export provides an interface to the tokenizer.
        // Because the tokenizer is optimized for being efficiently used by
        // the Acorn parser itself, this interface is somewhat crude and not
        // very modular. Performing another parse or call to `tokenize` will
        // reset the internal state, and invalidate existing tokenizers.

        exports.tokenize = function(inpt, opts) {
            input = String(inpt); inputLen = input.length;
            setOptions(opts);
            initTokenState();

            function getToken(forceRegexp) {
                lastEnd = tokEnd;
                readToken(forceRegexp);
                return new Token();
            }
            getToken.jumpTo = function(pos, reAllowed) {
                tokPos = pos;
                if (options.locations) {
                    tokCurLine = 1;
                    tokLineStart = lineBreak.lastIndex = 0;
                    var match;
                    while ((match = lineBreak.exec(input)) && match.index < pos) {
                        ++tokCurLine;
                        tokLineStart = match.index + match[0].length;
                    }
                }
                tokRegexpAllowed = reAllowed;
                skipSpace();
            };
            return getToken;
        };

        // State is kept in (closure-)global variables. We already saw the
        // `options`, `input`, and `inputLen` variables above.

        // The current position of the tokenizer in the input.

        var tokPos;

        // The start and end offsets of the current token.

        var tokStart, tokEnd;

        // When `options.locations` is true, these hold objects
        // containing the tokens start and end line/column pairs.

        var tokStartLoc, tokEndLoc;

        // The type and value of the current token. Token types are objects,
        // named by variables against which they can be compared, and
        // holding properties that describe them (indicating, for example,
        // the precedence of an infix operator, and the original name of a
        // keyword token). The kind of value that's held in `tokVal` depends
        // on the type of the token. For literals, it is the literal value,
        // for operators, the operator name, and so on.

        var tokType, tokVal;

        // Internal state for the tokenizer. To distinguish between division
        // operators and regular expressions, it remembers whether the last
        // token was one that is allowed to be followed by an expression.
        // (If it is, a slash is probably a regexp, if it isn't it's a
        // division operator. See the `parseStatement` function for a
        // caveat.)

        var tokRegexpAllowed;

        // When `options.locations` is true, these are used to keep
        // track of the current line, and know when a new line has been
        // entered.

        var tokCurLine, tokLineStart;

        // These store the position of the previous token, which is useful
        // when finishing a node and assigning its `end` position.

        var lastStart, lastEnd, lastEndLoc;

        // This is the parser's state. `inFunction` is used to reject
        // `return` statements outside of functions, `inGenerator` to
        // reject `yield`s outside of generators, `labels` to verify
        // that `break` and `continue` have somewhere to jump to, and
        // `strict` indicates whether strict mode is on.

        var inFunction, inGenerator, labels, strict;

        // This counter is used for checking that arrow expressions did
        // not contain nested parentheses in argument list.

        var metParenL;

        // This is used by parser for detecting if it's inside ES6
        // Template String. If it is, it should treat '$' as prefix before
        // '{expression}' and everything else as string literals.

        var inTemplate;

        // This function is used to raise exceptions on parse errors. It
        // takes an offset integer (into the current `input`) to indicate
        // the location of the error, attaches the position to the end
        // of the error message, and then raises a `SyntaxError` with that
        // message.

        function raise(pos, message) {
            var loc = getLineInfo(input, pos);
            message += " (" + loc.line + ":" + loc.column + ")";
            var err = new SyntaxError(message);
            err.pos = pos; err.loc = loc; err.raisedAt = tokPos;
            throw err;
        }

        // Reused empty array added for node fields that are always empty.

        var empty = [];

        // ## Token types

        // The assignment of fine-grained, information-carrying type objects
        // allows the tokenizer to store the information it has about a
        // token in a way that is very cheap for the parser to look up.

        // All token type variables start with an underscore, to make them
        // easy to recognize.

        // These are the general types. The `type` property is only used to
        // make them recognizeable when debugging.

        var _num = {type: "num"}, _regexp = {type: "regexp"}, _string = {type: "string"};
        var _name = {type: "name"}, _eof = {type: "eof"};

        // Keyword tokens. The `keyword` property (also used in keyword-like
        // operators) indicates that the token originated from an
        // identifier-like word, which is used when parsing property names.
        //
        // The `beforeExpr` property is used to disambiguate between regular
        // expressions and divisions. It is set on all token types that can
        // be followed by an expression (thus, a slash after them would be a
        // regular expression).
        //
        // `isLoop` marks a keyword as starting a loop, which is important
        // to know when parsing a label, in order to allow or disallow
        // continue jumps to that label.

        var _break = {keyword: "break"}, _case = {keyword: "case", beforeExpr: true}, _catch = {keyword: "catch"};
        var _continue = {keyword: "continue"}, _debugger = {keyword: "debugger"}, _default = {keyword: "default"};
        var _do = {keyword: "do", isLoop: true}, _else = {keyword: "else", beforeExpr: true};
        var _finally = {keyword: "finally"}, _for = {keyword: "for", isLoop: true}, _function = {keyword: "function"};
        var _if = {keyword: "if"}, _return = {keyword: "return", beforeExpr: true}, _switch = {keyword: "switch"};
        var _throw = {keyword: "throw", beforeExpr: true}, _try = {keyword: "try"}, _var = {keyword: "var"};
        var _let = {keyword: "let"}, _const = {keyword: "const"};
        var _while = {keyword: "while", isLoop: true}, _with = {keyword: "with"}, _new = {keyword: "new", beforeExpr: true};
        var _this = {keyword: "this"};
        var _class = {keyword: "class"}, _extends = {keyword: "extends", beforeExpr: true};
        var _export = {keyword: "export"}, _import = {keyword: "import"};
        var _yield = {keyword: "yield", beforeExpr: true};

        // The keywords that denote values.

        var _null = {keyword: "null", atomValue: null}, _true = {keyword: "true", atomValue: true};
        var _false = {keyword: "false", atomValue: false};

        // Some keywords are treated as regular operators. `in` sometimes
        // (when parsing `for`) needs to be tested against specifically, so
        // we assign a variable name to it for quick comparing.

        var _in = {keyword: "in", binop: 7, beforeExpr: true};

        // Map keyword names to token types.

        var keywordTypes = {"break": _break, "case": _case, "catch": _catch,
            "continue": _continue, "debugger": _debugger, "default": _default,
            "do": _do, "else": _else, "finally": _finally, "for": _for,
            "function": _function, "if": _if, "return": _return, "switch": _switch,
            "throw": _throw, "try": _try, "var": _var, "let": _let, "const": _const,
            "while": _while, "with": _with,
            "null": _null, "true": _true, "false": _false, "new": _new, "in": _in,
            "instanceof": {keyword: "instanceof", binop: 7, beforeExpr: true}, "this": _this,
            "typeof": {keyword: "typeof", prefix: true, beforeExpr: true},
            "void": {keyword: "void", prefix: true, beforeExpr: true},
            "delete": {keyword: "delete", prefix: true, beforeExpr: true},
            "class": _class, "extends": _extends,
            "export": _export, "import": _import, "yield": _yield};

        // Punctuation token types. Again, the `type` property is purely for debugging.

        var _bracketL = {type: "[", beforeExpr: true}, _bracketR = {type: "]"}, _braceL = {type: "{", beforeExpr: true};
        var _braceR = {type: "}"}, _parenL = {type: "(", beforeExpr: true}, _parenR = {type: ")"};
        var _comma = {type: ",", beforeExpr: true}, _semi = {type: ";", beforeExpr: true};
        var _colon = {type: ":", beforeExpr: true}, _dot = {type: "."}, _ellipsis = {type: "..."}, _question = {type: "?", beforeExpr: true};
        var _arrow = {type: "=>", beforeExpr: true}, _bquote = {type: "`"}, _dollarBraceL = {type: "${", beforeExpr: true};

        // Operators. These carry several kinds of properties to help the
        // parser use them properly (the presence of these properties is
        // what categorizes them as operators).
        //
        // `binop`, when present, specifies that this operator is a binary
        // operator, and will refer to its precedence.
        //
        // `prefix` and `postfix` mark the operator as a prefix or postfix
        // unary operator. `isUpdate` specifies that the node produced by
        // the operator should be of type UpdateExpression rather than
        // simply UnaryExpression (`++` and `--`).
        //
        // `isAssign` marks all of `=`, `+=`, `-=` etcetera, which act as
        // binary operators with a very low precedence, that should result
        // in AssignmentExpression nodes.

        var _slash = {binop: 10, beforeExpr: true}, _eq = {isAssign: true, beforeExpr: true};
        var _assign = {isAssign: true, beforeExpr: true};
        var _incDec = {postfix: true, prefix: true, isUpdate: true}, _prefix = {prefix: true, beforeExpr: true};
        var _logicalOR = {binop: 1, beforeExpr: true};
        var _logicalAND = {binop: 2, beforeExpr: true};
        var _bitwiseOR = {binop: 3, beforeExpr: true};
        var _bitwiseXOR = {binop: 4, beforeExpr: true};
        var _bitwiseAND = {binop: 5, beforeExpr: true};
        var _equality = {binop: 6, beforeExpr: true};
        var _relational = {binop: 7, beforeExpr: true};
        var _bitShift = {binop: 8, beforeExpr: true};
        var _plusMin = {binop: 9, prefix: true, beforeExpr: true};
        var _modulo = {binop: 10, beforeExpr: true};

        // '*' may be multiply or have special meaning in ES6
        var _star = {binop: 10, beforeExpr: true};

        // Provide access to the token types for external users of the
        // tokenizer.

        exports.tokTypes = {bracketL: _bracketL, bracketR: _bracketR, braceL: _braceL, braceR: _braceR,
            parenL: _parenL, parenR: _parenR, comma: _comma, semi: _semi, colon: _colon,
            dot: _dot, ellipsis: _ellipsis, question: _question, slash: _slash, eq: _eq,
            name: _name, eof: _eof, num: _num, regexp: _regexp, string: _string,
            arrow: _arrow, bquote: _bquote, dollarBraceL: _dollarBraceL};
        for (var kw in keywordTypes) exports.tokTypes["_" + kw] = keywordTypes[kw];

        // This is a trick taken from Esprima. It turns out that, on
        // non-Chrome browsers, to check whether a string is in a set, a
        // predicate containing a big ugly `switch` statement is faster than
        // a regular expression, and on Chrome the two are about on par.
        // This function uses `eval` (non-lexical) to produce such a
        // predicate from a space-separated string of words.
        //
        // It starts by sorting the words by length.

        function makePredicate(words) {
            words = words.split(" ");
            var f = "", cats = [];
            out: for (var i = 0; i < words.length; ++i) {
                for (var j = 0; j < cats.length; ++j)
                    if (cats[j][0].length == words[i].length) {
                        cats[j].push(words[i]);
                        continue out;
                    }
                cats.push([words[i]]);
            }
            function compareTo(arr) {
                if (arr.length == 1) return f += "return str === " + JSON.stringify(arr[0]) + ";";
                f += "switch(str){";
                for (var i = 0; i < arr.length; ++i) f += "case " + JSON.stringify(arr[i]) + ":";
                f += "return true}return false;";
            }

            // When there are more than three length categories, an outer
            // switch first dispatches on the lengths, to save on comparisons.

            if (cats.length > 3) {
                cats.sort(function(a, b) {return b.length - a.length;});
                f += "switch(str.length){";
                for (var i = 0; i < cats.length; ++i) {
                    var cat = cats[i];
                    f += "case " + cat[0].length + ":";
                    compareTo(cat);
                }
                f += "}";

                // Otherwise, simply generate a flat `switch` statement.

            } else {
                compareTo(words);
            }
            return new Function("str", f);
        }

        // The ECMAScript 3 reserved word list.

        var isReservedWord3 = makePredicate("abstract boolean byte char class double enum export extends final float goto implements import int interface long native package private protected public short static super synchronized throws transient volatile");

        // ECMAScript 5 reserved words.

        var isReservedWord5 = makePredicate("class enum extends super const export import");

        // The additional reserved words in strict mode.

        var isStrictReservedWord = makePredicate("implements interface let package private protected public static yield");

        // The forbidden variable names in strict mode.

        var isStrictBadIdWord = makePredicate("eval arguments");

        // And the keywords.

        var ecma5AndLessKeywords = "break case catch continue debugger default do else finally for function if return switch throw try var while with null true false instanceof typeof void delete new in this";

        var isEcma5AndLessKeyword = makePredicate(ecma5AndLessKeywords);

        var isEcma6Keyword = makePredicate(ecma5AndLessKeywords + " let const class extends export import yield");

        var isKeyword = isEcma5AndLessKeyword;

        // ## Character categories

        // Big ugly regular expressions that match characters in the
        // whitespace, identifier, and identifier-start categories. These
        // are only applied when a character is found to actually have a
        // code point above 128.
        // Generated by `tools/generate-identifier-regex.js`.

        var nonASCIIwhitespace = /[\u1680\u180e\u2000-\u200a\u202f\u205f\u3000\ufeff]/;
        var nonASCIIidentifierStartChars = "\xAA\xB5\xBA\xC0-\xD6\xD8-\xF6\xF8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u037F\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u052F\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0-\u08B2\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0980\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u16EE-\u16F8\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191E\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2160-\u2188\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA69D\uA6A0-\uA6EF\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA7AD\uA7B0\uA7B1\uA7F7-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uA9E0-\uA9E4\uA9E6-\uA9EF\uA9FA-\uA9FE\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA7E-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uAB30-\uAB5A\uAB5C-\uAB5F\uAB64\uAB65\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC";
        var nonASCIIidentifierChars = "\u0300-\u036F\u0483-\u0487\u0591-\u05BD\u05BF\u05C1\u05C2\u05C4\u05C5\u05C7\u0610-\u061A\u064B-\u0669\u0670\u06D6-\u06DC\u06DF-\u06E4\u06E7\u06E8\u06EA-\u06ED\u06F0-\u06F9\u0711\u0730-\u074A\u07A6-\u07B0\u07C0-\u07C9\u07EB-\u07F3\u0816-\u0819\u081B-\u0823\u0825-\u0827\u0829-\u082D\u0859-\u085B\u08E4-\u0903\u093A-\u093C\u093E-\u094F\u0951-\u0957\u0962\u0963\u0966-\u096F\u0981-\u0983\u09BC\u09BE-\u09C4\u09C7\u09C8\u09CB-\u09CD\u09D7\u09E2\u09E3\u09E6-\u09EF\u0A01-\u0A03\u0A3C\u0A3E-\u0A42\u0A47\u0A48\u0A4B-\u0A4D\u0A51\u0A66-\u0A71\u0A75\u0A81-\u0A83\u0ABC\u0ABE-\u0AC5\u0AC7-\u0AC9\u0ACB-\u0ACD\u0AE2\u0AE3\u0AE6-\u0AEF\u0B01-\u0B03\u0B3C\u0B3E-\u0B44\u0B47\u0B48\u0B4B-\u0B4D\u0B56\u0B57\u0B62\u0B63\u0B66-\u0B6F\u0B82\u0BBE-\u0BC2\u0BC6-\u0BC8\u0BCA-\u0BCD\u0BD7\u0BE6-\u0BEF\u0C00-\u0C03\u0C3E-\u0C44\u0C46-\u0C48\u0C4A-\u0C4D\u0C55\u0C56\u0C62\u0C63\u0C66-\u0C6F\u0C81-\u0C83\u0CBC\u0CBE-\u0CC4\u0CC6-\u0CC8\u0CCA-\u0CCD\u0CD5\u0CD6\u0CE2\u0CE3\u0CE6-\u0CEF\u0D01-\u0D03\u0D3E-\u0D44\u0D46-\u0D48\u0D4A-\u0D4D\u0D57\u0D62\u0D63\u0D66-\u0D6F\u0D82\u0D83\u0DCA\u0DCF-\u0DD4\u0DD6\u0DD8-\u0DDF\u0DE6-\u0DEF\u0DF2\u0DF3\u0E31\u0E34-\u0E3A\u0E47-\u0E4E\u0E50-\u0E59\u0EB1\u0EB4-\u0EB9\u0EBB\u0EBC\u0EC8-\u0ECD\u0ED0-\u0ED9\u0F18\u0F19\u0F20-\u0F29\u0F35\u0F37\u0F39\u0F3E\u0F3F\u0F71-\u0F84\u0F86\u0F87\u0F8D-\u0F97\u0F99-\u0FBC\u0FC6\u102B-\u103E\u1040-\u1049\u1056-\u1059\u105E-\u1060\u1062-\u1064\u1067-\u106D\u1071-\u1074\u1082-\u108D\u108F-\u109D\u135D-\u135F\u1712-\u1714\u1732-\u1734\u1752\u1753\u1772\u1773\u17B4-\u17D3\u17DD\u17E0-\u17E9\u180B-\u180D\u1810-\u1819\u18A9\u1920-\u192B\u1930-\u193B\u1946-\u194F\u19B0-\u19C0\u19C8\u19C9\u19D0-\u19D9\u1A17-\u1A1B\u1A55-\u1A5E\u1A60-\u1A7C\u1A7F-\u1A89\u1A90-\u1A99\u1AB0-\u1ABD\u1B00-\u1B04\u1B34-\u1B44\u1B50-\u1B59\u1B6B-\u1B73\u1B80-\u1B82\u1BA1-\u1BAD\u1BB0-\u1BB9\u1BE6-\u1BF3\u1C24-\u1C37\u1C40-\u1C49\u1C50-\u1C59\u1CD0-\u1CD2\u1CD4-\u1CE8\u1CED\u1CF2-\u1CF4\u1CF8\u1CF9\u1DC0-\u1DF5\u1DFC-\u1DFF\u200C\u200D\u203F\u2040\u2054\u20D0-\u20DC\u20E1\u20E5-\u20F0\u2CEF-\u2CF1\u2D7F\u2DE0-\u2DFF\u302A-\u302F\u3099\u309A\uA620-\uA629\uA66F\uA674-\uA67D\uA69F\uA6F0\uA6F1\uA802\uA806\uA80B\uA823-\uA827\uA880\uA881\uA8B4-\uA8C4\uA8D0-\uA8D9\uA8E0-\uA8F1\uA900-\uA909\uA926-\uA92D\uA947-\uA953\uA980-\uA983\uA9B3-\uA9C0\uA9D0-\uA9D9\uA9E5\uA9F0-\uA9F9\uAA29-\uAA36\uAA43\uAA4C\uAA4D\uAA50-\uAA59\uAA7B-\uAA7D\uAAB0\uAAB2-\uAAB4\uAAB7\uAAB8\uAABE\uAABF\uAAC1\uAAEB-\uAAEF\uAAF5\uAAF6\uABE3-\uABEA\uABEC\uABED\uABF0-\uABF9\uFB1E\uFE00-\uFE0F\uFE20-\uFE2D\uFE33\uFE34\uFE4D-\uFE4F\uFF10-\uFF19\uFF3F";
        var nonASCIIidentifierStart = new RegExp("[" + nonASCIIidentifierStartChars + "]");
        var nonASCIIidentifier = new RegExp("[" + nonASCIIidentifierStartChars + nonASCIIidentifierChars + "]");

        // Whether a single character denotes a newline.

        var newline = /[\n\r\u2028\u2029]/;

        // Matches a whole line break (where CRLF is considered a single
        // line break). Used to count lines.

        var lineBreak = /\r\n|[\n\r\u2028\u2029]/g;

        // Test whether a given character code starts an identifier.

        var isIdentifierStart = exports.isIdentifierStart = function(code) {
            if (code < 65) return code === 36;
            if (code < 91) return true;
            if (code < 97) return code === 95;
            if (code < 123)return true;
            return code >= 0xaa && nonASCIIidentifierStart.test(String.fromCharCode(code));
        };

        // Test whether a given character is part of an identifier.

        var isIdentifierChar = exports.isIdentifierChar = function(code) {
            if (code < 48) return code === 36;
            if (code < 58) return true;
            if (code < 65) return false;
            if (code < 91) return true;
            if (code < 97) return code === 95;
            if (code < 123)return true;
            return code >= 0xaa && nonASCIIidentifier.test(String.fromCharCode(code));
        };

        // ## Tokenizer

        // These are used when `options.locations` is on, for the
        // `tokStartLoc` and `tokEndLoc` properties.

        function Position() {
            this.line = tokCurLine;
            this.column = tokPos - tokLineStart;
        }

        // Reset the token state. Used at the start of a parse.

        function initTokenState() {
            tokCurLine = 1;
            tokPos = tokLineStart = 0;
            tokRegexpAllowed = true;
            metParenL = 0;
            inTemplate = false;
            skipSpace();
        }

        // Called at the end of every token. Sets `tokEnd`, `tokVal`, and
        // `tokRegexpAllowed`, and skips the space after the token, so that
        // the next one's `tokStart` will point at the right position.

        function finishToken(type, val, shouldSkipSpace) {
            tokEnd = tokPos;
            if (options.locations) tokEndLoc = new Position;
            tokType = type;
            if (shouldSkipSpace !== false) skipSpace();
            tokVal = val;
            tokRegexpAllowed = type.beforeExpr;
            if (options.onToken) {
                options.onToken(new Token());
            }
        }

        function skipBlockComment() {
            var startLoc = options.onComment && options.locations && new Position;
            var start = tokPos, end = input.indexOf("*/", tokPos += 2);
            if (end === -1) raise(tokPos - 2, "Unterminated comment");
            tokPos = end + 2;
            if (options.locations) {
                lineBreak.lastIndex = start;
                var match;
                while ((match = lineBreak.exec(input)) && match.index < tokPos) {
                    ++tokCurLine;
                    tokLineStart = match.index + match[0].length;
                }
            }
            if (options.onComment)
                options.onComment(true, input.slice(start + 2, end), start, tokPos,
                    startLoc, options.locations && new Position);
        }

        function skipLineComment(startSkip) {
            var start = tokPos;
            var startLoc = options.onComment && options.locations && new Position;
            var ch = input.charCodeAt(tokPos+=startSkip);
            while (tokPos < inputLen && ch !== 10 && ch !== 13 && ch !== 8232 && ch !== 8233) {
                ++tokPos;
                ch = input.charCodeAt(tokPos);
            }
            if (options.onComment)
                options.onComment(false, input.slice(start + startSkip, tokPos), start, tokPos,
                    startLoc, options.locations && new Position);
        }

        // Called at the start of the parse and after every token. Skips
        // whitespace and comments, and.

        function skipSpace() {
            while (tokPos < inputLen) {
                var ch = input.charCodeAt(tokPos);
                if (ch === 32) { // ' '
                    ++tokPos;
                } else if (ch === 13) {
                    ++tokPos;
                    var next = input.charCodeAt(tokPos);
                    if (next === 10) {
                        ++tokPos;
                    }
                    if (options.locations) {
                        ++tokCurLine;
                        tokLineStart = tokPos;
                    }
                } else if (ch === 10 || ch === 8232 || ch === 8233) {
                    ++tokPos;
                    if (options.locations) {
                        ++tokCurLine;
                        tokLineStart = tokPos;
                    }
                } else if (ch > 8 && ch < 14) {
                    ++tokPos;
                } else if (ch === 47) { // '/'
                    var next = input.charCodeAt(tokPos + 1);
                    if (next === 42) { // '*'
                        skipBlockComment();
                    } else if (next === 47) { // '/'
                        skipLineComment(2);
                    } else break;
                } else if (ch === 160) { // '\xa0'
                    ++tokPos;
                } else if (ch >= 5760 && nonASCIIwhitespace.test(String.fromCharCode(ch))) {
                    ++tokPos;
                } else {
                    break;
                }
            }
        }

        // ### Token reading

        // This is the function that is called to fetch the next token. It
        // is somewhat obscure, because it works in character codes rather
        // than characters, and because operator parsing has been inlined
        // into it.
        //
        // All in the name of speed.
        //
        // The `forceRegexp` parameter is used in the one case where the
        // `tokRegexpAllowed` trick does not work. See `parseStatement`.

        function readToken_dot() {
            var next = input.charCodeAt(tokPos + 1);
            if (next >= 48 && next <= 57) return readNumber(true);
            var next2 = input.charCodeAt(tokPos + 2);
            if (options.ecmaVersion >= 6 && next === 46 && next2 === 46) { // 46 = dot '.'
                tokPos += 3;
                return finishToken(_ellipsis);
            } else {
                ++tokPos;
                return finishToken(_dot);
            }
        }

        function readToken_slash() { // '/'
            var next = input.charCodeAt(tokPos + 1);
            if (tokRegexpAllowed) {++tokPos; return readRegexp();}
            if (next === 61) return finishOp(_assign, 2);
            return finishOp(_slash, 1);
        }

        function readToken_mult_modulo(code) { // '%*'
            var next = input.charCodeAt(tokPos + 1);
            if (next === 61) return finishOp(_assign, 2);
            return finishOp(code === 42 ? _star : _modulo, 1);
        }

        function readToken_pipe_amp(code) { // '|&'
            var next = input.charCodeAt(tokPos + 1);
            if (next === code) return finishOp(code === 124 ? _logicalOR : _logicalAND, 2);
            if (next === 61) return finishOp(_assign, 2);
            return finishOp(code === 124 ? _bitwiseOR : _bitwiseAND, 1);
        }

        function readToken_caret() { // '^'
            var next = input.charCodeAt(tokPos + 1);
            if (next === 61) return finishOp(_assign, 2);
            return finishOp(_bitwiseXOR, 1);
        }

        function readToken_plus_min(code) { // '+-'
            var next = input.charCodeAt(tokPos + 1);
            if (next === code) {
                if (next == 45 && input.charCodeAt(tokPos + 2) == 62 &&
                    newline.test(input.slice(lastEnd, tokPos))) {
                    // A `-->` line comment
                    skipLineComment(3);
                    skipSpace();
                    return readToken();
                }
                return finishOp(_incDec, 2);
            }
            if (next === 61) return finishOp(_assign, 2);
            return finishOp(_plusMin, 1);
        }

        function readToken_lt_gt(code) { // '<>'
            var next = input.charCodeAt(tokPos + 1);
            var size = 1;
            if (next === code) {
                size = code === 62 && input.charCodeAt(tokPos + 2) === 62 ? 3 : 2;
                if (input.charCodeAt(tokPos + size) === 61) return finishOp(_assign, size + 1);
                return finishOp(_bitShift, size);
            }
            if (next == 33 && code == 60 && input.charCodeAt(tokPos + 2) == 45 &&
                input.charCodeAt(tokPos + 3) == 45) {
                // `<!--`, an XML-style comment that should be interpreted as a line comment
                skipLineComment(4);
                skipSpace();
                return readToken();
            }
            if (next === 61)
                size = input.charCodeAt(tokPos + 2) === 61 ? 3 : 2;
            return finishOp(_relational, size);
        }

        function readToken_eq_excl(code) { // '=!', '=>'
            var next = input.charCodeAt(tokPos + 1);
            if (next === 61) return finishOp(_equality, input.charCodeAt(tokPos + 2) === 61 ? 3 : 2);
            if (code === 61 && next === 62 && options.ecmaVersion >= 6) { // '=>'
                tokPos += 2;
                return finishToken(_arrow);
            }
            return finishOp(code === 61 ? _eq : _prefix, 1);
        }

        // Get token inside ES6 template (special rules work there).

        function getTemplateToken(code) {
            // '`' and '${' have special meanings, but they should follow
            // string (can be empty)
            if (tokType === _string) {
                if (code === 96) { // '`'
                    ++tokPos;
                    return finishToken(_bquote);
                } else
                if (code === 36 && input.charCodeAt(tokPos + 1) === 123) { // '${'
                    tokPos += 2;
                    return finishToken(_dollarBraceL);
                }
            }

            if (code === 125) { // '}'
                ++tokPos;
                return finishToken(_braceR, undefined, false);
            }

            // anything else is considered string literal
            return readTmplString();
        }

        function getTokenFromCode(code) {
            switch (code) {
                // The interpretation of a dot depends on whether it is followed
                // by a digit or another two dots.
                case 46: // '.'
                    return readToken_dot();

                // Punctuation tokens.
                case 40: ++tokPos; return finishToken(_parenL);
                case 41: ++tokPos; return finishToken(_parenR);
                case 59: ++tokPos; return finishToken(_semi);
                case 44: ++tokPos; return finishToken(_comma);
                case 91: ++tokPos; return finishToken(_bracketL);
                case 93: ++tokPos; return finishToken(_bracketR);
                case 123: ++tokPos; return finishToken(_braceL);
                case 125: ++tokPos; return finishToken(_braceR);
                case 58: ++tokPos; return finishToken(_colon);
                case 63: ++tokPos; return finishToken(_question);

                case 96: // '`'
                    if (options.ecmaVersion >= 6) {
                        ++tokPos;
                        return finishToken(_bquote, undefined, false);
                    }

                case 48: // '0'
                    var next = input.charCodeAt(tokPos + 1);
                    if (next === 120 || next === 88) return readRadixNumber(16); // '0x', '0X' - hex number
                    if (options.ecmaVersion >= 6) {
                        if (next === 111 || next === 79) return readRadixNumber(8); // '0o', '0O' - octal number
                        if (next === 98 || next === 66) return readRadixNumber(2); // '0b', '0B' - binary number
                    }
                // Anything else beginning with a digit is an integer, octal
                // number, or float.
                case 49: case 50: case 51: case 52: case 53: case 54: case 55: case 56: case 57: // 1-9
                return readNumber(false);

                // Quotes produce strings.
                case 34: case 39: // '"', "'"
                return readString(code);

                // Operators are parsed inline in tiny state machines. '=' (61) is
                // often referred to. `finishOp` simply skips the amount of
                // characters it is given as second argument, and returns a token
                // of the type given by its first argument.

                case 47: // '/'
                    return readToken_slash();

                case 37: case 42: // '%*'
                return readToken_mult_modulo(code);

                case 124: case 38: // '|&'
                return readToken_pipe_amp(code);

                case 94: // '^'
                    return readToken_caret();

                case 43: case 45: // '+-'
                return readToken_plus_min(code);

                case 60: case 62: // '<>'
                return readToken_lt_gt(code);

                case 61: case 33: // '=!'
                return readToken_eq_excl(code);

                case 126: // '~'
                    return finishOp(_prefix, 1);
            }

            return false;
        }

        function readToken(forceRegexp) {
            if (!forceRegexp) tokStart = tokPos;
            else tokPos = tokStart + 1;
            if (options.locations) tokStartLoc = new Position;
            if (forceRegexp) return readRegexp();
            if (tokPos >= inputLen) return finishToken(_eof);

            var code = input.charCodeAt(tokPos);

            if (inTemplate) return getTemplateToken(code);

            // Identifier or keyword. '\uXXXX' sequences are allowed in
            // identifiers, so '\' also dispatches to that.
            if (isIdentifierStart(code) || code === 92 /* '\' */) return readWord();

            var tok = getTokenFromCode(code);

            if (tok === false) {
                // If we are here, we either found a non-ASCII identifier
                // character, or something that's entirely disallowed.
                var ch = String.fromCharCode(code);
                if (ch === "\\" || nonASCIIidentifierStart.test(ch)) return readWord();
                raise(tokPos, "Unexpected character '" + ch + "'");
            }
            return tok;
        }

        function finishOp(type, size) {
            var str = input.slice(tokPos, tokPos + size);
            tokPos += size;
            finishToken(type, str);
        }

        // Parse a regular expression. Some context-awareness is necessary,
        // since a '/' inside a '[]' set does not end the expression.

        function readRegexp() {
            var content = "", escaped, inClass, start = tokPos;
            for (;;) {
                if (tokPos >= inputLen) raise(start, "Unterminated regular expression");
                var ch = input.charAt(tokPos);
                if (newline.test(ch)) raise(start, "Unterminated regular expression");
                if (!escaped) {
                    if (ch === "[") inClass = true;
                    else if (ch === "]" && inClass) inClass = false;
                    else if (ch === "/" && !inClass) break;
                    escaped = ch === "\\";
                } else escaped = false;
                ++tokPos;
            }
            var content = input.slice(start, tokPos);
            ++tokPos;
            // Need to use `readWord1` because '\uXXXX' sequences are allowed
            // here (don't ask).
            var mods = readWord1();
            if (mods && !/^[gmsiy]*$/.test(mods)) raise(start, "Invalid regular expression flag");
            try {
                var value = new RegExp(content, mods);
            } catch (e) {
                if (e instanceof SyntaxError) raise(start, "Error parsing regular expression: " + e.message);
                raise(e);
            }
            return finishToken(_regexp, value);
        }

        // Read an integer in the given radix. Return null if zero digits
        // were read, the integer value otherwise. When `len` is given, this
        // will return `null` unless the integer has exactly `len` digits.

        function readInt(radix, len) {
            var start = tokPos, total = 0;
            for (var i = 0, e = len == null ? Infinity : len; i < e; ++i) {
                var code = input.charCodeAt(tokPos), val;
                if (code >= 97) val = code - 97 + 10; // a
                else if (code >= 65) val = code - 65 + 10; // A
                else if (code >= 48 && code <= 57) val = code - 48; // 0-9
                else val = Infinity;
                if (val >= radix) break;
                ++tokPos;
                total = total * radix + val;
            }
            if (tokPos === start || len != null && tokPos - start !== len) return null;

            return total;
        }

        function readRadixNumber(radix) {
            tokPos += 2; // 0x
            var val = readInt(radix);
            if (val == null) raise(tokStart + 2, "Expected number in radix " + radix);
            if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number");
            return finishToken(_num, val);
        }

        // Read an integer, octal integer, or floating-point number.

        function readNumber(startsWithDot) {
            var start = tokPos, isFloat = false, octal = input.charCodeAt(tokPos) === 48;
            if (!startsWithDot && readInt(10) === null) raise(start, "Invalid number");
            if (input.charCodeAt(tokPos) === 46) {
                ++tokPos;
                readInt(10);
                isFloat = true;
            }
            var next = input.charCodeAt(tokPos);
            if (next === 69 || next === 101) { // 'eE'
                next = input.charCodeAt(++tokPos);
                if (next === 43 || next === 45) ++tokPos; // '+-'
                if (readInt(10) === null) raise(start, "Invalid number");
                isFloat = true;
            }
            if (isIdentifierStart(input.charCodeAt(tokPos))) raise(tokPos, "Identifier directly after number");

            var str = input.slice(start, tokPos), val;
            if (isFloat) val = parseFloat(str);
            else if (!octal || str.length === 1) val = parseInt(str, 10);
            else if (/[89]/.test(str) || strict) raise(start, "Invalid number");
            else val = parseInt(str, 8);
            return finishToken(_num, val);
        }

        // Read a string value, interpreting backslash-escapes.

        function readCodePoint() {
            var ch = input.charCodeAt(tokPos), code;

            if (ch === 123) {
                if (options.ecmaVersion < 6) unexpected();
                ++tokPos;
                code = readHexChar(input.indexOf('}', tokPos) - tokPos);
                ++tokPos;
                if (code > 0x10FFFF) unexpected();
            } else {
                code = readHexChar(4);
            }

            // UTF-16 Encoding
            if (code <= 0xFFFF) {
                return String.fromCharCode(code);
            }
            var cu1 = ((code - 0x10000) >> 10) + 0xD800;
            var cu2 = ((code - 0x10000) & 1023) + 0xDC00;
            return String.fromCharCode(cu1, cu2);
        }

        function readString(quote) {
            ++tokPos;
            var out = "";
            for (;;) {
                if (tokPos >= inputLen) raise(tokStart, "Unterminated string constant");
                var ch = input.charCodeAt(tokPos);
                if (ch === quote) {
                    ++tokPos;
                    return finishToken(_string, out);
                }
                if (ch === 92) { // '\'
                    out += readEscapedChar();
                } else {
                    ++tokPos;
                    if (newline.test(String.fromCharCode(ch))) {
                        raise(tokStart, "Unterminated string constant");
                    }
                    out += String.fromCharCode(ch); // '\'
                }
            }
        }

        function readTmplString() {
            var out = "";
            for (;;) {
                if (tokPos >= inputLen) raise(tokStart, "Unterminated string constant");
                var ch = input.charCodeAt(tokPos);
                if (ch === 96 || ch === 36 && input.charCodeAt(tokPos + 1) === 123) // '`', '${'
                    return finishToken(_string, out);
                if (ch === 92) { // '\'
                    out += readEscapedChar();
                } else {
                    ++tokPos;
                    if (newline.test(String.fromCharCode(ch))) {
                        if (ch === 13 && input.charCodeAt(tokPos) === 10) {
                            ++tokPos;
                            ch = 10;
                        }
                        if (options.locations) {
                            ++tokCurLine;
                            tokLineStart = tokPos;
                        }
                    }
                    out += String.fromCharCode(ch); // '\'
                }
            }
        }

        // Used to read escaped characters

        function readEscapedChar() {
            var ch = input.charCodeAt(++tokPos);
            var octal = /^[0-7]+/.exec(input.slice(tokPos, tokPos + 3));
            if (octal) octal = octal[0];
            while (octal && parseInt(octal, 8) > 255) octal = octal.slice(0, -1);
            if (octal === "0") octal = null;
            ++tokPos;
            if (octal) {
                if (strict) raise(tokPos - 2, "Octal literal in strict mode");
                tokPos += octal.length - 1;
                return String.fromCharCode(parseInt(octal, 8));
            } else {
                switch (ch) {
                    case 110: return "\n"; // 'n' -> '\n'
                    case 114: return "\r"; // 'r' -> '\r'
                    case 120: return String.fromCharCode(readHexChar(2)); // 'x'
                    case 117: return readCodePoint(); // 'u'
                    case 85: return String.fromCharCode(readHexChar(8)); // 'U'
                    case 116: return "\t"; // 't' -> '\t'
                    case 98: return "\b"; // 'b' -> '\b'
                    case 118: return "\u000b"; // 'v' -> '\u000b'
                    case 102: return "\f"; // 'f' -> '\f'
                    case 48: return "\0"; // 0 -> '\0'
                    case 13: if (input.charCodeAt(tokPos) === 10) ++tokPos; // '\r\n'
                    case 10: // ' \n'
                        if (options.locations) { tokLineStart = tokPos; ++tokCurLine; }
                        return "";
                    default: return String.fromCharCode(ch);
                }
            }
        }

        // Used to read character escape sequences ('\x', '\u', '\U').

        function readHexChar(len) {
            var n = readInt(16, len);
            if (n === null) raise(tokStart, "Bad character escape sequence");
            return n;
        }

        // Used to signal to callers of `readWord1` whether the word
        // contained any escape sequences. This is needed because words with
        // escape sequences must not be interpreted as keywords.

        var containsEsc;

        // Read an identifier, and return it as a string. Sets `containsEsc`
        // to whether the word contained a '\u' escape.
        //
        // Only builds up the word character-by-character when it actually
        // containeds an escape, as a micro-optimization.

        function readWord1() {
            containsEsc = false;
            var word, first = true, start = tokPos;
            for (;;) {
                var ch = input.charCodeAt(tokPos);
                if (isIdentifierChar(ch)) {
                    if (containsEsc) word += input.charAt(tokPos);
                    ++tokPos;
                } else if (ch === 92) { // "\"
                    if (!containsEsc) word = input.slice(start, tokPos);
                    containsEsc = true;
                    if (input.charCodeAt(++tokPos) != 117) // "u"
                        raise(tokPos, "Expecting Unicode escape sequence \\uXXXX");
                    ++tokPos;
                    var esc = readHexChar(4);
                    var escStr = String.fromCharCode(esc);
                    if (!escStr) raise(tokPos - 1, "Invalid Unicode escape");
                    if (!(first ? isIdentifierStart(esc) : isIdentifierChar(esc)))
                        raise(tokPos - 4, "Invalid Unicode escape");
                    word += escStr;
                } else {
                    break;
                }
                first = false;
            }
            return containsEsc ? word : input.slice(start, tokPos);
        }

        // Read an identifier or keyword token. Will check for reserved
        // words when necessary.

        function readWord() {
            var word = readWord1();
            var type = _name;
            if (!containsEsc && isKeyword(word))
                type = keywordTypes[word];
            return finishToken(type, word);
        }

        // ## Parser

        // A recursive descent parser operates by defining functions for all
        // syntactic elements, and recursively calling those, each function
        // advancing the input stream and returning an AST node. Precedence
        // of constructs (for example, the fact that `!x[1]` means `!(x[1])`
        // instead of `(!x)[1]` is handled by the fact that the parser
        // function that parses unary prefix operators is called first, and
        // in turn calls the function that parses `[]` subscripts — that
        // way, it'll receive the node for `x[1]` already parsed, and wraps
        // *that* in the unary operator node.
        //
        // Acorn uses an [operator precedence parser][opp] to handle binary
        // operator precedence, because it is much more compact than using
        // the technique outlined above, which uses different, nesting
        // functions to specify precedence, for all of the ten binary
        // precedence levels that JavaScript defines.
        //
        // [opp]: http://en.wikipedia.org/wiki/Operator-precedence_parser

        // ### Parser utilities

        // Continue to the next token.

        function next() {
            lastStart = tokStart;
            lastEnd = tokEnd;
            lastEndLoc = tokEndLoc;
            readToken();
        }

        // Enter strict mode. Re-reads the next token to please pedantic
        // tests ("use strict"; 010; -- should fail).

        function setStrict(strct) {
            strict = strct;
            tokPos = tokStart;
            if (options.locations) {
                while (tokPos < tokLineStart) {
                    tokLineStart = input.lastIndexOf("\n", tokLineStart - 2) + 1;
                    --tokCurLine;
                }
            }
            skipSpace();
            readToken();
        }

        // Start an AST node, attaching a start offset.

        function Node() {
            this.type = null;
            this.start = tokStart;
            this.end = null;
        }

        exports.Node = Node;

        function SourceLocation() {
            this.start = tokStartLoc;
            this.end = null;
            if (sourceFile !== null) this.source = sourceFile;
        }

        function startNode() {
            var node = new Node();
            if (options.locations)
                node.loc = new SourceLocation();
            if (options.directSourceFile)
                node.sourceFile = options.directSourceFile;
            if (options.ranges)
                node.range = [tokStart, 0];
            return node;
        }

        // Start a node whose start offset information should be based on
        // the start of another node. For example, a binary operator node is
        // only started after its left-hand side has already been parsed.

        function startNodeFrom(other) {
            var node = new Node();
            node.start = other.start;
            if (options.locations) {
                node.loc = new SourceLocation();
                node.loc.start = other.loc.start;
            }
            if (options.ranges)
                node.range = [other.range[0], 0];

            return node;
        }

        // Finish an AST node, adding `type` and `end` properties.

        function finishNode(node, type) {
            node.type = type;
            node.end = lastEnd;
            if (options.locations)
                node.loc.end = lastEndLoc;
            if (options.ranges)
                node.range[1] = lastEnd;
            return node;
        }

        // Test whether a statement node is the string literal `"use strict"`.

        function isUseStrict(stmt) {
            return options.ecmaVersion >= 5 && stmt.type === "ExpressionStatement" &&
                stmt.expression.type === "Literal" && stmt.expression.value === "use strict";
        }

        // Predicate that tests whether the next token is of the given
        // type, and if yes, consumes it as a side effect.

        function eat(type) {
            if (tokType === type) {
                next();
                return true;
            } else {
                return false;
            }
        }

        // Test whether a semicolon can be inserted at the current position.

        function canInsertSemicolon() {
            return !options.strictSemicolons &&
                (tokType === _eof || tokType === _braceR || newline.test(input.slice(lastEnd, tokStart)));
        }

        // Consume a semicolon, or, failing that, see if we are allowed to
        // pretend that there is a semicolon at this position.

        function semicolon() {
            if (!eat(_semi) && !canInsertSemicolon()) unexpected();
        }

        // Expect a token of a given type. If found, consume it, otherwise,
        // raise an unexpected token error.

        function expect(type) {
            eat(type) || unexpected();
        }

        // Raise an unexpected token error.

        function unexpected(pos) {
            raise(pos != null ? pos : tokStart, "Unexpected token");
        }

        // Checks if hash object has a property.

        function has(obj, propName) {
            return Object.prototype.hasOwnProperty.call(obj, propName);
        }
        // Convert existing expression atom to assignable pattern
        // if possible.

        function toAssignable(node, allowSpread, checkType) {
            if (options.ecmaVersion >= 6 && node) {
                switch (node.type) {
                    case "Identifier":
                    case "MemberExpression":
                        break;

                    case "ObjectExpression":
                        node.type = "ObjectPattern";
                        for (var i = 0; i < node.properties.length; i++) {
                            var prop = node.properties[i];
                            if (prop.kind !== "init") unexpected(prop.key.start);
                            toAssignable(prop.value, false, checkType);
                        }
                        break;

                    case "ArrayExpression":
                        node.type = "ArrayPattern";
                        for (var i = 0, lastI = node.elements.length - 1; i <= lastI; i++) {
                            toAssignable(node.elements[i], i === lastI, checkType);
                        }
                        break;

                    case "SpreadElement":
                        if (allowSpread) {
                            toAssignable(node.argument, false, checkType);
                            checkSpreadAssign(node.argument);
                        } else {
                            unexpected(node.start);
                        }
                        break;

                    default:
                        if (checkType) unexpected(node.start);
                }
            }
            return node;
        }

        // Checks if node can be assignable spread argument.

        function checkSpreadAssign(node) {
            if (node.type !== "Identifier" && node.type !== "ArrayPattern")
                unexpected(node.start);
        }

        // Verify that argument names are not repeated, and it does not
        // try to bind the words `eval` or `arguments`.

        function checkFunctionParam(param, nameHash) {
            switch (param.type) {
                case "Identifier":
                    if (isStrictReservedWord(param.name) || isStrictBadIdWord(param.name))
                        raise(param.start, "Defining '" + param.name + "' in strict mode");
                    if (has(nameHash, param.name))
                        raise(param.start, "Argument name clash in strict mode");
                    nameHash[param.name] = true;
                    break;

                case "ObjectPattern":
                    for (var i = 0; i < param.properties.length; i++)
                        checkFunctionParam(param.properties[i].value, nameHash);
                    break;

                case "ArrayPattern":
                    for (var i = 0; i < param.elements.length; i++)
                        checkFunctionParam(param.elements[i], nameHash);
                    break;
            }
        }

        // Check if property name clashes with already added.
        // Object/class getters and setters are not allowed to clash —
        // either with each other or with an init property — and in
        // strict mode, init properties are also not allowed to be repeated.

        function checkPropClash(prop, propHash) {
            if (prop.computed) return;
            var key = prop.key, name;
            switch (key.type) {
                case "Identifier": name = key.name; break;
                case "Literal": name = String(key.value); break;
                default: return;
            }
            var kind = prop.kind || "init", other;
            if (has(propHash, name)) {
                other = propHash[name];
                var isGetSet = kind !== "init";
                if ((strict || isGetSet) && other[kind] || !(isGetSet ^ other.init))
                    raise(key.start, "Redefinition of property");
            } else {
                other = propHash[name] = {
                    init: false,
                    get: false,
                    set: false
                };
            }
            other[kind] = true;
        }

        // Verify that a node is an lval — something that can be assigned
        // to.

        function checkLVal(expr, isBinding) {
            switch (expr.type) {
                case "Identifier":
                    if (strict && (isStrictBadIdWord(expr.name) || isStrictReservedWord(expr.name)))
                        raise(expr.start, isBinding
                                ? "Binding " + expr.name + " in strict mode"
                                : "Assigning to " + expr.name + " in strict mode"
                        );
                    break;

                case "MemberExpression":
                    if (!isBinding) break;

                case "ObjectPattern":
                    for (var i = 0; i < expr.properties.length; i++)
                        checkLVal(expr.properties[i].value, isBinding);
                    break;

                case "ArrayPattern":
                    for (var i = 0; i < expr.elements.length; i++) {
                        var elem = expr.elements[i];
                        if (elem) checkLVal(elem, isBinding);
                    }
                    break;

                case "SpreadElement":
                    break;

                default:
                    raise(expr.start, "Assigning to rvalue");
            }
        }

        // ### Statement parsing

        // Parse a program. Initializes the parser, reads any number of
        // statements, and wraps them in a Program node.  Optionally takes a
        // `program` argument.  If present, the statements will be appended
        // to its body instead of creating a new node.

        function parseTopLevel(program) {
            lastStart = lastEnd = tokPos;
            if (options.locations) lastEndLoc = new Position;
            inFunction = inGenerator = strict = null;
            labels = [];
            readToken();

            var node = program || startNode(), first = true;
            if (!program) node.body = [];
            while (tokType !== _eof) {
                var stmt = parseStatement();
                node.body.push(stmt);
                if (first && isUseStrict(stmt)) setStrict(true);
                first = false;
            }
            return finishNode(node, "Program");
        }

        var loopLabel = {kind: "loop"}, switchLabel = {kind: "switch"};

        // Parse a single statement.
        //
        // If expecting a statement and finding a slash operator, parse a
        // regular expression literal. This is to handle cases like
        // `if (foo) /blah/.exec(foo);`, where looking at the previous token
        // does not help.

        function parseStatement() {
            if (tokType === _slash || tokType === _assign && tokVal == "/=")
                readToken(true);

            var starttype = tokType, node = startNode();

            // Most types of statements are recognized by the keyword they
            // start with. Many are trivial to parse, some require a bit of
            // complexity.

            switch (starttype) {
                case _break: case _continue: return parseBreakContinueStatement(node, starttype.keyword);
                case _debugger: return parseDebuggerStatement(node);
                case _do: return parseDoStatement(node);
                case _for: return parseForStatement(node);
                case _function: return parseFunctionStatement(node);
                case _class: return parseClass(node, true);
                case _if: return parseIfStatement(node);
                case _return: return parseReturnStatement(node);
                case _switch: return parseSwitchStatement(node);
                case _throw: return parseThrowStatement(node);
                case _try: return parseTryStatement(node);
                case _var: case _let: case _const: return parseVarStatement(node, starttype.keyword);
                case _while: return parseWhileStatement(node);
                case _with: return parseWithStatement(node);
                case _braceL: return parseBlock(); // no point creating a function for this
                case _semi: return parseEmptyStatement(node);
                case _export: return parseExport(node);
                case _import: return parseImport(node);

                // If the statement does not start with a statement keyword or a
                // brace, it's an ExpressionStatement or LabeledStatement. We
                // simply start parsing an expression, and afterwards, if the
                // next token is a colon and the expression was a simple
                // Identifier node, we switch to interpreting it as a label.
                default:
                    var maybeName = tokVal, expr = parseExpression();
                    if (starttype === _name && expr.type === "Identifier" && eat(_colon))
                        return parseLabeledStatement(node, maybeName, expr);
                    else return parseExpressionStatement(node, expr);
            }
        }

        function parseBreakContinueStatement(node, keyword) {
            var isBreak = keyword == "break";
            next();
            if (eat(_semi) || canInsertSemicolon()) node.label = null;
            else if (tokType !== _name) unexpected();
            else {
                node.label = parseIdent();
                semicolon();
            }

            // Verify that there is an actual destination to break or
            // continue to.
            for (var i = 0; i < labels.length; ++i) {
                var lab = labels[i];
                if (node.label == null || lab.name === node.label.name) {
                    if (lab.kind != null && (isBreak || lab.kind === "loop")) break;
                    if (node.label && isBreak) break;
                }
            }
            if (i === labels.length) raise(node.start, "Unsyntactic " + keyword);
            return finishNode(node, isBreak ? "BreakStatement" : "ContinueStatement");
        }

        function parseDebuggerStatement(node) {
            next();
            semicolon();
            return finishNode(node, "DebuggerStatement");
        }

        function parseDoStatement(node) {
            next();
            labels.push(loopLabel);
            node.body = parseStatement();
            labels.pop();
            expect(_while);
            node.test = parseParenExpression();
            semicolon();
            return finishNode(node, "DoWhileStatement");
        }

        // Disambiguating between a `for` and a `for`/`in` or `for`/`of`
        // loop is non-trivial. Basically, we have to parse the init `var`
        // statement or expression, disallowing the `in` operator (see
        // the second parameter to `parseExpression`), and then check
        // whether the next token is `in` or `of`. When there is no init
        // part (semicolon immediately after the opening parenthesis), it
        // is a regular `for` loop.

        function parseForStatement(node) {
            next();
            labels.push(loopLabel);
            expect(_parenL);
            if (tokType === _semi) return parseFor(node, null);
            if (tokType === _var || tokType === _let) {
                var init = startNode(), varKind = tokType.keyword, isLet = tokType === _let;
                next();
                parseVar(init, true, varKind);
                finishNode(init, "VariableDeclaration");
                if ((tokType === _in || (tokType === _name && tokVal === "of")) && init.declarations.length === 1 &&
                    !(isLet && init.declarations[0].init))
                    return parseForIn(node, init);
                return parseFor(node, init);
            }
            var init = parseExpression(false, true);
            if (tokType === _in || (tokType === _name && tokVal === "of")) {
                checkLVal(init);
                return parseForIn(node, init);
            }
            return parseFor(node, init);
        }

        function parseFunctionStatement(node) {
            next();
            return parseFunction(node, true);
        }

        function parseIfStatement(node) {
            next();
            node.test = parseParenExpression();
            node.consequent = parseStatement();
            node.alternate = eat(_else) ? parseStatement() : null;
            return finishNode(node, "IfStatement");
        }

        function parseReturnStatement(node) {
            if (!inFunction && !options.allowReturnOutsideFunction)
                raise(tokStart, "'return' outside of function");
            next();

            // In `return` (and `break`/`continue`), the keywords with
            // optional arguments, we eagerly look for a semicolon or the
            // possibility to insert one.

            if (eat(_semi) || canInsertSemicolon()) node.argument = null;
            else { node.argument = parseExpression(); semicolon(); }
            return finishNode(node, "ReturnStatement");
        }

        function parseSwitchStatement(node) {
            next();
            node.discriminant = parseParenExpression();
            node.cases = [];
            expect(_braceL);
            labels.push(switchLabel);

            // Statements under must be grouped (by label) in SwitchCase
            // nodes. `cur` is used to keep the node that we are currently
            // adding statements to.

            for (var cur, sawDefault; tokType != _braceR;) {
                if (tokType === _case || tokType === _default) {
                    var isCase = tokType === _case;
                    if (cur) finishNode(cur, "SwitchCase");
                    node.cases.push(cur = startNode());
                    cur.consequent = [];
                    next();
                    if (isCase) cur.test = parseExpression();
                    else {
                        if (sawDefault) raise(lastStart, "Multiple default clauses"); sawDefault = true;
                        cur.test = null;
                    }
                    expect(_colon);
                } else {
                    if (!cur) unexpected();
                    cur.consequent.push(parseStatement());
                }
            }
            if (cur) finishNode(cur, "SwitchCase");
            next(); // Closing brace
            labels.pop();
            return finishNode(node, "SwitchStatement");
        }

        function parseThrowStatement(node) {
            next();
            if (newline.test(input.slice(lastEnd, tokStart)))
                raise(lastEnd, "Illegal newline after throw");
            node.argument = parseExpression();
            semicolon();
            return finishNode(node, "ThrowStatement");
        }

        function parseTryStatement(node) {
            next();
            node.block = parseBlock();
            node.handler = null;
            if (tokType === _catch) {
                var clause = startNode();
                next();
                expect(_parenL);
                clause.param = parseIdent();
                if (strict && isStrictBadIdWord(clause.param.name))
                    raise(clause.param.start, "Binding " + clause.param.name + " in strict mode");
                expect(_parenR);
                clause.guard = null;
                clause.body = parseBlock();
                node.handler = finishNode(clause, "CatchClause");
            }
            node.guardedHandlers = empty;
            node.finalizer = eat(_finally) ? parseBlock() : null;
            if (!node.handler && !node.finalizer)
                raise(node.start, "Missing catch or finally clause");
            return finishNode(node, "TryStatement");
        }

        function parseVarStatement(node, kind) {
            next();
            parseVar(node, false, kind);
            semicolon();
            return finishNode(node, "VariableDeclaration");
        }

        function parseWhileStatement(node) {
            next();
            node.test = parseParenExpression();
            labels.push(loopLabel);
            node.body = parseStatement();
            labels.pop();
            return finishNode(node, "WhileStatement");
        }

        function parseWithStatement(node) {
            if (strict) raise(tokStart, "'with' in strict mode");
            next();
            node.object = parseParenExpression();
            node.body = parseStatement();
            return finishNode(node, "WithStatement");
        }

        function parseEmptyStatement(node) {
            next();
            return finishNode(node, "EmptyStatement");
        }

        function parseLabeledStatement(node, maybeName, expr) {
            for (var i = 0; i < labels.length; ++i)
                if (labels[i].name === maybeName) raise(expr.start, "Label '" + maybeName + "' is already declared");
            var kind = tokType.isLoop ? "loop" : tokType === _switch ? "switch" : null;
            labels.push({name: maybeName, kind: kind});
            node.body = parseStatement();
            labels.pop();
            node.label = expr;
            return finishNode(node, "LabeledStatement");
        }

        function parseExpressionStatement(node, expr) {
            node.expression = expr;
            semicolon();
            return finishNode(node, "ExpressionStatement");
        }

        // Used for constructs like `switch` and `if` that insist on
        // parentheses around their expression.

        function parseParenExpression() {
            expect(_parenL);
            var val = parseExpression();
            expect(_parenR);
            return val;
        }

        // Parse a semicolon-enclosed block of statements, handling `"use
        // strict"` declarations when `allowStrict` is true (used for
        // function bodies).

        function parseBlock(allowStrict) {
            var node = startNode(), first = true, strict = false, oldStrict;
            node.body = [];
            expect(_braceL);
            while (!eat(_braceR)) {
                var stmt = parseStatement();
                node.body.push(stmt);
                if (first && allowStrict && isUseStrict(stmt)) {
                    oldStrict = strict;
                    setStrict(strict = true);
                }
                first = false;
            }
            if (strict && !oldStrict) setStrict(false);
            return finishNode(node, "BlockStatement");
        }

        // Parse a regular `for` loop. The disambiguation code in
        // `parseStatement` will already have parsed the init statement or
        // expression.

        function parseFor(node, init) {
            node.init = init;
            expect(_semi);
            node.test = tokType === _semi ? null : parseExpression();
            expect(_semi);
            node.update = tokType === _parenR ? null : parseExpression();
            expect(_parenR);
            node.body = parseStatement();
            labels.pop();
            return finishNode(node, "ForStatement");
        }

        // Parse a `for`/`in` and `for`/`of` loop, which are almost
        // same from parser's perspective.

        function parseForIn(node, init) {
            var type = tokType === _in ? "ForInStatement" : "ForOfStatement";
            next();
            node.left = init;
            node.right = parseExpression();
            expect(_parenR);
            node.body = parseStatement();
            labels.pop();
            return finishNode(node, type);
        }

        // Parse a list of variable declarations.

        function parseVar(node, noIn, kind) {
            node.declarations = [];
            node.kind = kind;
            for (;;) {
                var decl = startNode();
                decl.id = options.ecmaVersion >= 6 ? toAssignable(parseExprAtom()) : parseIdent();
                checkLVal(decl.id, true);
                decl.init = eat(_eq) ? parseExpression(true, noIn) : (kind === _const.keyword ? unexpected() : null);
                node.declarations.push(finishNode(decl, "VariableDeclarator"));
                if (!eat(_comma)) break;
            }
            return node;
        }

        // ### Expression parsing

        // These nest, from the most general expression type at the top to
        // 'atomic', nondivisible expression types at the bottom. Most of
        // the functions will simply let the function(s) below them parse,
        // and, *if* the syntactic construct they handle is present, wrap
        // the AST node that the inner parser gave them in another node.

        // Parse a full expression. The arguments are used to forbid comma
        // sequences (in argument lists, array literals, or object literals)
        // or the `in` operator (in for loops initalization expressions).

        function parseExpression(noComma, noIn) {
            var expr = parseMaybeAssign(noIn);
            if (!noComma && tokType === _comma) {
                var node = startNodeFrom(expr);
                node.expressions = [expr];
                while (eat(_comma)) node.expressions.push(parseMaybeAssign(noIn));
                return finishNode(node, "SequenceExpression");
            }
            return expr;
        }

        // Parse an assignment expression. This includes applications of
        // operators like `+=`.

        function parseMaybeAssign(noIn) {
            var left = parseMaybeConditional(noIn);
            if (tokType.isAssign) {
                var node = startNodeFrom(left);
                node.operator = tokVal;
                node.left = tokType === _eq ? toAssignable(left) : left;
                checkLVal(left);
                next();
                node.right = parseMaybeAssign(noIn);
                return finishNode(node, "AssignmentExpression");
            }
            return left;
        }

        // Parse a ternary conditional (`?:`) operator.

        function parseMaybeConditional(noIn) {
            var expr = parseExprOps(noIn);
            if (eat(_question)) {
                var node = startNodeFrom(expr);
                node.test = expr;
                node.consequent = parseExpression(true);
                expect(_colon);
                node.alternate = parseExpression(true, noIn);
                return finishNode(node, "ConditionalExpression");
            }
            return expr;
        }

        // Start the precedence parser.

        function parseExprOps(noIn) {
            return parseExprOp(parseMaybeUnary(), -1, noIn);
        }

        // Parse binary operators with the operator precedence parsing
        // algorithm. `left` is the left-hand side of the operator.
        // `minPrec` provides context that allows the function to stop and
        // defer further parser to one of its callers when it encounters an
        // operator that has a lower precedence than the set it is parsing.

        function parseExprOp(left, minPrec, noIn) {
            var prec = tokType.binop;
            if (prec != null && (!noIn || tokType !== _in)) {
                if (prec > minPrec) {
                    var node = startNodeFrom(left);
                    node.left = left;
                    node.operator = tokVal;
                    var op = tokType;
                    next();
                    node.right = parseExprOp(parseMaybeUnary(), prec, noIn);
                    var exprNode = finishNode(node, (op === _logicalOR || op === _logicalAND) ? "LogicalExpression" : "BinaryExpression");
                    return parseExprOp(exprNode, minPrec, noIn);
                }
            }
            return left;
        }

        // Parse unary operators, both prefix and postfix.

        function parseMaybeUnary() {
            if (tokType.prefix) {
                var node = startNode(), update = tokType.isUpdate;
                node.operator = tokVal;
                node.prefix = true;
                tokRegexpAllowed = true;
                next();
                node.argument = parseMaybeUnary();
                if (update) checkLVal(node.argument);
                else if (strict && node.operator === "delete" &&
                    node.argument.type === "Identifier")
                    raise(node.start, "Deleting local variable in strict mode");
                return finishNode(node, update ? "UpdateExpression" : "UnaryExpression");
            }
            var expr = parseExprSubscripts();
            while (tokType.postfix && !canInsertSemicolon()) {
                var node = startNodeFrom(expr);
                node.operator = tokVal;
                node.prefix = false;
                node.argument = expr;
                checkLVal(expr);
                next();
                expr = finishNode(node, "UpdateExpression");
            }
            return expr;
        }

        // Parse call, dot, and `[]`-subscript expressions.

        function parseExprSubscripts() {
            return parseSubscripts(parseExprAtom());
        }

        function parseSubscripts(base, noCalls) {
            if (eat(_dot)) {
                var node = startNodeFrom(base);
                node.object = base;
                node.property = parseIdent(true);
                node.computed = false;
                return parseSubscripts(finishNode(node, "MemberExpression"), noCalls);
            } else if (eat(_bracketL)) {
                var node = startNodeFrom(base);
                node.object = base;
                node.property = parseExpression();
                node.computed = true;
                expect(_bracketR);
                return parseSubscripts(finishNode(node, "MemberExpression"), noCalls);
            } else if (!noCalls && eat(_parenL)) {
                var node = startNodeFrom(base);
                node.callee = base;
                node.arguments = parseExprList(_parenR, false);
                return parseSubscripts(finishNode(node, "CallExpression"), noCalls);
            } else if (tokType === _bquote) {
                var node = startNodeFrom(base);
                node.tag = base;
                node.quasi = parseTemplate();
                return parseSubscripts(finishNode(node, "TaggedTemplateExpression"), noCalls);
            } return base;
        }

        // Parse an atomic expression — either a single token that is an
        // expression, an expression started by a keyword like `function` or
        // `new`, or an expression wrapped in punctuation like `()`, `[]`,
        // or `{}`.

        function parseExprAtom() {
            switch (tokType) {
                case _this:
                    var node = startNode();
                    next();
                    return finishNode(node, "ThisExpression");

                case _yield:
                    if (inGenerator) return parseYield();

                case _name:
                    var id = parseIdent(tokType !== _name);
                    if (eat(_arrow)) {
                        return parseArrowExpression(startNodeFrom(id), [id]);
                    }
                    return id;

                case _num: case _string: case _regexp:
                var node = startNode();
                node.value = tokVal;
                node.raw = input.slice(tokStart, tokEnd);
                next();
                return finishNode(node, "Literal");

                case _null: case _true: case _false:
                var node = startNode();
                node.value = tokType.atomValue;
                node.raw = tokType.keyword;
                next();
                return finishNode(node, "Literal");

                case _parenL:
                    var tokStartLoc1 = tokStartLoc, tokStart1 = tokStart, val, exprList;
                    next();
                    // check whether this is generator comprehension or regular expression
                    if (options.ecmaVersion >= 6 && tokType === _for) {
                        val = parseComprehension(startNode(), true);
                    } else {
                        var oldParenL = ++metParenL;
                        if (tokType !== _parenR) {
                            val = parseExpression();
                            exprList = val.type === "SequenceExpression" ? val.expressions : [val];
                        } else {
                            exprList = [];
                        }
                        expect(_parenR);
                        // if '=>' follows '(...)', convert contents to arguments
                        if (metParenL === oldParenL && eat(_arrow)) {
                            val = parseArrowExpression(startNode(), exprList);
                        } else {
                            // forbid '()' before everything but '=>'
                            if (!val) unexpected(lastStart);
                            // forbid '...' in sequence expressions
                            if (options.ecmaVersion >= 6) {
                                for (var i = 0; i < exprList.length; i++) {
                                    if (exprList[i].type === "SpreadElement") unexpected();
                                }
                            }
                        }
                    }
                    val.start = tokStart1;
                    val.end = lastEnd;
                    if (options.locations) {
                        val.loc.start = tokStartLoc1;
                        val.loc.end = lastEndLoc;
                    }
                    if (options.ranges) {
                        val.range = [tokStart1, lastEnd];
                    }
                    return val;

                case _bracketL:
                    var node = startNode();
                    next();
                    // check whether this is array comprehension or regular array
                    if (options.ecmaVersion >= 6 && tokType === _for) {
                        return parseComprehension(node, false);
                    }
                    node.elements = parseExprList(_bracketR, true, true);
                    return finishNode(node, "ArrayExpression");

                case _braceL:
                    return parseObj();

                case _function:
                    var node = startNode();
                    next();
                    return parseFunction(node, false);

                case _class:
                    return parseClass(startNode(), false);

                case _new:
                    return parseNew();

                case _ellipsis:
                    return parseSpread();

                case _bquote:
                    return parseTemplate();

                default:
                    unexpected();
            }
        }

        // New's precedence is slightly tricky. It must allow its argument
        // to be a `[]` or dot subscript expression, but not a call — at
        // least, not without wrapping it in parentheses. Thus, it uses the

        function parseNew() {
            var node = startNode();
            next();
            node.callee = parseSubscripts(parseExprAtom(), true);
            if (eat(_parenL)) node.arguments = parseExprList(_parenR, false);
            else node.arguments = empty;
            return finishNode(node, "NewExpression");
        }

        // Parse spread element '...expr'

        function parseSpread() {
            var node = startNode();
            next();
            node.argument = parseExpression(true);
            return finishNode(node, "SpreadElement");
        }

        // Parse template expression.

        function parseTemplate() {
            var node = startNode();
            node.expressions = [];
            node.quasis = [];
            inTemplate = true;
            next();
            for (;;) {
                var elem = startNode();
                elem.value = {cooked: tokVal, raw: input.slice(tokStart, tokEnd)};
                elem.tail = false;
                next();
                node.quasis.push(finishNode(elem, "TemplateElement"));
                if (eat(_bquote)) { // '`', end of template
                    elem.tail = true;
                    break;
                }
                inTemplate = false;
                expect(_dollarBraceL);
                node.expressions.push(parseExpression());
                inTemplate = true;
                expect(_braceR);
            }
            inTemplate = false;
            return finishNode(node, "TemplateLiteral");
        }

        // Parse an object literal.

        function parseObj() {
            var node = startNode(), first = true, propHash = {};
            node.properties = [];
            next();
            while (!eat(_braceR)) {
                if (!first) {
                    expect(_comma);
                    if (options.allowTrailingCommas && eat(_braceR)) break;
                } else first = false;

                var prop = startNode(), isGenerator;
                if (options.ecmaVersion >= 6) {
                    prop.method = false;
                    prop.shorthand = false;
                    isGenerator = eat(_star);
                }
                parsePropertyName(prop);
                if (eat(_colon)) {
                    prop.value = parseExpression(true);
                    prop.kind = "init";
                } else if (options.ecmaVersion >= 6 && tokType === _parenL) {
                    prop.kind = "init";
                    prop.method = true;
                    prop.value = parseMethod(isGenerator);
                } else if (options.ecmaVersion >= 5 && !prop.computed && prop.key.type === "Identifier" &&
                    (prop.key.name === "get" || prop.key.name === "set")) {
                    if (isGenerator) unexpected();
                    prop.kind = prop.key.name;
                    parsePropertyName(prop);
                    prop.value = parseMethod(false);
                } else if (options.ecmaVersion >= 6 && !prop.computed && prop.key.type === "Identifier") {
                    prop.kind = "init";
                    prop.value = prop.key;
                    prop.shorthand = true;
                } else unexpected();

                checkPropClash(prop, propHash);
                node.properties.push(finishNode(prop, "Property"));
            }
            return finishNode(node, "ObjectExpression");
        }

        function parsePropertyName(prop) {
            if (options.ecmaVersion >= 6) {
                if (eat(_bracketL)) {
                    prop.computed = true;
                    prop.key = parseExpression();
                    expect(_bracketR);
                    return;
                } else {
                    prop.computed = false;
                }
            }
            prop.key = (tokType === _num || tokType === _string) ? parseExprAtom() : parseIdent(true);
        }

        // Initialize empty function node.

        function initFunction(node) {
            node.id = null;
            node.params = [];
            if (options.ecmaVersion >= 6) {
                node.defaults = [];
                node.rest = null;
                node.generator = false;
            }
        }

        // Parse a function declaration or literal (depending on the
        // `isStatement` parameter).

        function parseFunction(node, isStatement, allowExpressionBody) {
            initFunction(node);
            if (options.ecmaVersion >= 6) {
                node.generator = eat(_star);
            }
            if (isStatement || tokType === _name) {
                node.id = parseIdent();
            }
            parseFunctionParams(node);
            parseFunctionBody(node, allowExpressionBody);
            return finishNode(node, isStatement ? "FunctionDeclaration" : "FunctionExpression");
        }

        // Parse object or class method.

        function parseMethod(isGenerator) {
            var node = startNode();
            initFunction(node);
            parseFunctionParams(node);
            var allowExpressionBody;
            if (options.ecmaVersion >= 6) {
                node.generator = isGenerator;
                allowExpressionBody = true;
            } else {
                allowExpressionBody = false;
            }
            parseFunctionBody(node, allowExpressionBody);
            return finishNode(node, "FunctionExpression");
        }

        // Parse arrow function expression with given parameters.

        function parseArrowExpression(node, params) {
            initFunction(node);

            var defaults = node.defaults, hasDefaults = false;

            for (var i = 0, lastI = params.length - 1; i <= lastI; i++) {
                var param = params[i];

                if (param.type === "AssignmentExpression" && param.operator === "=") {
                    hasDefaults = true;
                    params[i] = param.left;
                    defaults.push(param.right);
                } else {
                    toAssignable(param, i === lastI, true);
                    defaults.push(null);
                    if (param.type === "SpreadElement") {
                        params.length--;
                        node.rest = param.argument;
                        break;
                    }
                }
            }

            node.params = params;
            if (!hasDefaults) node.defaults = [];

            parseFunctionBody(node, true);
            return finishNode(node, "ArrowFunctionExpression");
        }

        // Parse function parameters.

        function parseFunctionParams(node) {
            var defaults = [], hasDefaults = false;

            expect(_parenL);
            for (;;) {
                if (eat(_parenR)) {
                    break;
                } else if (options.ecmaVersion >= 6 && eat(_ellipsis)) {
                    node.rest = toAssignable(parseExprAtom(), false, true);
                    checkSpreadAssign(node.rest);
                    expect(_parenR);
                    break;
                } else {
                    node.params.push(options.ecmaVersion >= 6 ? toAssignable(parseExprAtom(), false, true) : parseIdent());
                    if (options.ecmaVersion >= 6 && tokType === _eq) {
                        next();
                        hasDefaults = true;
                        defaults.push(parseExpression(true));
                    }
                    if (!eat(_comma)) {
                        expect(_parenR);
                        break;
                    }
                }
            }

            if (hasDefaults) node.defaults = defaults;
        }

        // Parse function body and check parameters.

        function parseFunctionBody(node, allowExpression) {
            var isExpression = allowExpression && tokType !== _braceL;

            if (isExpression) {
                node.body = parseExpression(true);
                node.expression = true;
            } else {
                // Start a new scope with regard to labels and the `inFunction`
                // flag (restore them to their old value afterwards).
                var oldInFunc = inFunction, oldInGen = inGenerator, oldLabels = labels;
                inFunction = true; inGenerator = node.generator; labels = [];
                node.body = parseBlock(true);
                node.expression = false;
                inFunction = oldInFunc; inGenerator = oldInGen; labels = oldLabels;
            }

            // If this is a strict mode function, verify that argument names
            // are not repeated, and it does not try to bind the words `eval`
            // or `arguments`.
            if (strict || !isExpression && node.body.body.length && isUseStrict(node.body.body[0])) {
                var nameHash = {};
                if (node.id)
                    checkFunctionParam(node.id, {});
                for (var i = 0; i < node.params.length; i++)
                    checkFunctionParam(node.params[i], nameHash);
                if (node.rest)
                    checkFunctionParam(node.rest, nameHash);
            }
        }

        // Parse a class declaration or literal (depending on the
        // `isStatement` parameter).

        function parseClass(node, isStatement) {
            next();
            node.id = tokType === _name ? parseIdent() : isStatement ? unexpected() : null;
            node.superClass = eat(_extends) ? parseExpression() : null;
            var classBody = startNode(), methodHash = {}, staticMethodHash = {};
            classBody.body = [];
            expect(_braceL);
            while (!eat(_braceR)) {
                var method = startNode();
                if (tokType === _name && tokVal === "static") {
                    next();
                    method['static'] = true;
                } else {
                    method['static'] = false;
                }
                var isGenerator = eat(_star);
                parsePropertyName(method);
                if (tokType === _name && !method.computed && method.key.type === "Identifier" &&
                    (method.key.name === "get" || method.key.name === "set")) {
                    if (isGenerator) unexpected();
                    method.kind = method.key.name;
                    parsePropertyName(method);
                } else {
                    method.kind = "";
                }
                method.value = parseMethod(isGenerator);
                checkPropClash(method, method['static'] ? staticMethodHash : methodHash);
                classBody.body.push(finishNode(method, "MethodDefinition"));
                eat(_semi);
            }
            node.body = finishNode(classBody, "ClassBody");
            return finishNode(node, isStatement ? "ClassDeclaration" : "ClassExpression");
        }

        // Parses a comma-separated list of expressions, and returns them as
        // an array. `close` is the token type that ends the list, and
        // `allowEmpty` can be turned on to allow subsequent commas with
        // nothing in between them to be parsed as `null` (which is needed
        // for array literals).

        function parseExprList(close, allowTrailingComma, allowEmpty) {
            var elts = [], first = true;
            while (!eat(close)) {
                if (!first) {
                    expect(_comma);
                    if (allowTrailingComma && options.allowTrailingCommas && eat(close)) break;
                } else first = false;

                if (allowEmpty && tokType === _comma) elts.push(null);
                else elts.push(parseExpression(true));
            }
            return elts;
        }

        // Parse the next token as an identifier. If `liberal` is true (used
        // when parsing properties), it will also convert keywords into
        // identifiers.

        function parseIdent(liberal) {
            var node = startNode();
            if (liberal && options.forbidReserved == "everywhere") liberal = false;
            if (tokType === _name) {
                if (!liberal &&
                    (options.forbidReserved &&
                    (options.ecmaVersion === 3 ? isReservedWord3 : isReservedWord5)(tokVal) ||
                    strict && isStrictReservedWord(tokVal)) &&
                    input.slice(tokStart, tokEnd).indexOf("\\") == -1)
                    raise(tokStart, "The keyword '" + tokVal + "' is reserved");
                node.name = tokVal;
            } else if (liberal && tokType.keyword) {
                node.name = tokType.keyword;
            } else {
                unexpected();
            }
            tokRegexpAllowed = false;
            next();
            return finishNode(node, "Identifier");
        }

        // Parses module export declaration.

        function parseExport(node) {
            next();
            // export var|const|let|function|class ...;
            if (tokType === _var || tokType === _const || tokType === _let || tokType === _function || tokType === _class) {
                node.declaration = parseStatement();
                node['default'] = false;
                node.specifiers = null;
                node.source = null;
            } else
            // export default ...;
            if (eat(_default)) {
                node.declaration = parseExpression(true);
                node['default'] = true;
                node.specifiers = null;
                node.source = null;
                semicolon();
            } else {
                // export * from '...'
                // export { x, y as z } [from '...']
                var isBatch = tokType === _star;
                node.declaration = null;
                node['default'] = false;
                node.specifiers = parseExportSpecifiers();
                if (tokType === _name && tokVal === "from") {
                    next();
                    node.source = tokType === _string ? parseExprAtom() : unexpected();
                } else {
                    if (isBatch) unexpected();
                    node.source = null;
                }
            }
            return finishNode(node, "ExportDeclaration");
        }

        // Parses a comma-separated list of module exports.

        function parseExportSpecifiers() {
            var nodes = [], first = true;
            if (tokType === _star) {
                // export * from '...'
                var node = startNode();
                next();
                nodes.push(finishNode(node, "ExportBatchSpecifier"));
            } else {
                // export { x, y as z } [from '...']
                expect(_braceL);
                while (!eat(_braceR)) {
                    if (!first) {
                        expect(_comma);
                        if (options.allowTrailingCommas && eat(_braceR)) break;
                    } else first = false;

                    var node = startNode();
                    node.id = parseIdent();
                    if (tokType === _name && tokVal === "as") {
                        next();
                        node.name = parseIdent(true);
                    } else {
                        node.name = null;
                    }
                    nodes.push(finishNode(node, "ExportSpecifier"));
                }
            }
            return nodes;
        }

        // Parses import declaration.

        function parseImport(node) {
            next();
            // import '...';
            if (tokType === _string) {
                node.specifiers = [];
                node.source = parseExprAtom();
                node.kind = "";
            } else {
                node.specifiers = parseImportSpecifiers();
                if (tokType !== _name || tokVal !== "from") unexpected();
                next();
                node.source = tokType === _string ? parseExprAtom() : unexpected();
                // only for backward compatibility with Esprima's AST
                // (it doesn't support mixed default + named yet)
                node.kind = node.specifiers[0]['default'] ? "default" : "named";
            }
            return finishNode(node, "ImportDeclaration");
        }

        // Parses a comma-separated list of module imports.

        function parseImportSpecifiers() {
            var nodes = [], first = true;
            if (tokType === _star) {
                var node = startNode();
                next();
                if (tokType !== _name || tokVal !== "as") unexpected();
                next();
                node.name = parseIdent();
                checkLVal(node.name, true);
                nodes.push(finishNode(node, "ImportBatchSpecifier"));
                return nodes;
            }
            if (tokType === _name) {
                // import defaultObj, { x, y as z } from '...'
                var node = startNode();
                node.id = parseIdent();
                checkLVal(node.id, true);
                node.name = null;
                node['default'] = true;
                nodes.push(finishNode(node, "ImportSpecifier"));
                if (!eat(_comma)) return nodes;
            }
            expect(_braceL);
            while (!eat(_braceR)) {
                if (!first) {
                    expect(_comma);
                    if (options.allowTrailingCommas && eat(_braceR)) break;
                } else first = false;

                var node = startNode();
                node.id = parseIdent(true);
                if (tokType === _name && tokVal === "as") {
                    next();
                    node.name = parseIdent();
                } else {
                    node.name = null;
                }
                checkLVal(node.name || node.id, true);
                node['default'] = false;
                nodes.push(finishNode(node, "ImportSpecifier"));
            }
            return nodes;
        }

        // Parses yield expression inside generator.

        function parseYield() {
            var node = startNode();
            next();
            if (eat(_semi) || canInsertSemicolon()) {
                node.delegate = false;
                node.argument = null;
            } else {
                node.delegate = eat(_star);
                node.argument = parseExpression(true);
            }
            return finishNode(node, "YieldExpression");
        }

        // Parses array and generator comprehensions.

        function parseComprehension(node, isGenerator) {
            node.blocks = [];
            while (tokType === _for) {
                var block = startNode();
                next();
                expect(_parenL);
                block.left = toAssignable(parseExprAtom());
                checkLVal(block.left, true);
                if (tokType !== _name || tokVal !== "of") unexpected();
                next();
                // `of` property is here for compatibility with Esprima's AST
                // which also supports deprecated [for (... in ...) expr]
                block.of = true;
                block.right = parseExpression();
                expect(_parenR);
                node.blocks.push(finishNode(block, "ComprehensionBlock"));
            }
            node.filter = eat(_if) ? parseParenExpression() : null;
            node.body = parseExpression();
            expect(isGenerator ? _parenR : _bracketR);
            node.generator = isGenerator;
            return finishNode(node, "ComprehensionExpression");
        }

    });

},{}],33:[function(require,module,exports){
// AST walker module for Mozilla Parser API compatible trees

    (function(mod) {
        if (typeof exports == "object" && typeof module == "object") return mod(exports); // CommonJS
        if (typeof define == "function" && define.amd) return define(["exports"], mod); // AMD
        mod((this.acorn || (this.acorn = {})).walk = {}); // Plain browser env
    })(function(exports) {
        "use strict";

        // A simple walk is one where you simply specify callbacks to be
        // called on specific nodes. The last two arguments are optional. A
        // simple use would be
        //
        //     walk.simple(myTree, {
        //         Expression: function(node) { ... }
        //     });
        //
        // to do something with all expressions. All Parser API node types
        // can be used to identify node types, as well as Expression,
        // Statement, and ScopeBody, which denote categories of nodes.
        //
        // The base argument can be used to pass a custom (recursive)
        // walker, and state can be used to give this walked an initial
        // state.
        exports.simple = function(node, visitors, base, state) {
            if (!base) base = exports.base;
            function c(node, st, override) {
                var type = override || node.type, found = visitors[type];
                base[type](node, st, c);
                if (found) found(node, st);
            }
            c(node, state);
        };

        // An ancestor walk builds up an array of ancestor nodes (including
        // the current node) and passes them to the callback as the state parameter.
        exports.ancestor = function(node, visitors, base, state) {
            if (!base) base = exports.base;
            if (!state) state = [];
            function c(node, st, override) {
                var type = override || node.type, found = visitors[type];
                if (node != st[st.length - 1]) {
                    st = st.slice();
                    st.push(node);
                }
                base[type](node, st, c);
                if (found) found(node, st);
            }
            c(node, state);
        };

        // A recursive walk is one where your functions override the default
        // walkers. They can modify and replace the state parameter that's
        // threaded through the walk, and can opt how and whether to walk
        // their child nodes (by calling their third argument on these
        // nodes).
        exports.recursive = function(node, state, funcs, base) {
            var visitor = funcs ? exports.make(funcs, base) : base;
            function c(node, st, override) {
                visitor[override || node.type](node, st, c);
            }
            c(node, state);
        };

        function makeTest(test) {
            if (typeof test == "string")
                return function(type) { return type == test; };
            else if (!test)
                return function() { return true; };
            else
                return test;
        }

        function Found(node, state) { this.node = node; this.state = state; }

        // Find a node with a given start, end, and type (all are optional,
        // null can be used as wildcard). Returns a {node, state} object, or
        // undefined when it doesn't find a matching node.
        exports.findNodeAt = function(node, start, end, test, base, state) {
            test = makeTest(test);
            try {
                if (!base) base = exports.base;
                var c = function(node, st, override) {
                    var type = override || node.type;
                    if ((start == null || node.start <= start) &&
                        (end == null || node.end >= end))
                        base[type](node, st, c);
                    if (test(type, node) &&
                        (start == null || node.start == start) &&
                        (end == null || node.end == end))
                        throw new Found(node, st);
                };
                c(node, state);
            } catch (e) {
                if (e instanceof Found) return e;
                throw e;
            }
        };

        // Find the innermost node of a given type that contains the given
        // position. Interface similar to findNodeAt.
        exports.findNodeAround = function(node, pos, test, base, state) {
            test = makeTest(test);
            try {
                if (!base) base = exports.base;
                var c = function(node, st, override) {
                    var type = override || node.type;
                    if (node.start > pos || node.end < pos) return;
                    base[type](node, st, c);
                    if (test(type, node)) throw new Found(node, st);
                };
                c(node, state);
            } catch (e) {
                if (e instanceof Found) return e;
                throw e;
            }
        };

        // Find the outermost matching node after a given position.
        exports.findNodeAfter = function(node, pos, test, base, state) {
            test = makeTest(test);
            try {
                if (!base) base = exports.base;
                var c = function(node, st, override) {
                    if (node.end < pos) return;
                    var type = override || node.type;
                    if (node.start >= pos && test(type, node)) throw new Found(node, st);
                    base[type](node, st, c);
                };
                c(node, state);
            } catch (e) {
                if (e instanceof Found) return e;
                throw e;
            }
        };

        // Find the outermost matching node before a given position.
        exports.findNodeBefore = function(node, pos, test, base, state) {
            test = makeTest(test);
            if (!base) base = exports.base;
            var max;
            var c = function(node, st, override) {
                if (node.start > pos) return;
                var type = override || node.type;
                if (node.end <= pos && (!max || max.node.end < node.end) && test(type, node))
                    max = new Found(node, st);
                base[type](node, st, c);
            };
            c(node, state);
            return max;
        };

        // Used to create a custom walker. Will fill in all missing node
        // type properties with the defaults.
        exports.make = function(funcs, base) {
            if (!base) base = exports.base;
            var visitor = {};
            for (var type in base) visitor[type] = base[type];
            for (var type in funcs) visitor[type] = funcs[type];
            return visitor;
        };

        function skipThrough(node, st, c) { c(node, st); }
        function ignore(_node, _st, _c) {}

        // Node walkers.

        var base = exports.base = {};
        base.Program = base.BlockStatement = function(node, st, c) {
            for (var i = 0; i < node.body.length; ++i)
                c(node.body[i], st, "Statement");
        };
        base.Statement = skipThrough;
        base.EmptyStatement = ignore;
        base.ExpressionStatement = function(node, st, c) {
            c(node.expression, st, "Expression");
        };
        base.IfStatement = function(node, st, c) {
            c(node.test, st, "Expression");
            c(node.consequent, st, "Statement");
            if (node.alternate) c(node.alternate, st, "Statement");
        };
        base.LabeledStatement = function(node, st, c) {
            c(node.body, st, "Statement");
        };
        base.BreakStatement = base.ContinueStatement = ignore;
        base.WithStatement = function(node, st, c) {
            c(node.object, st, "Expression");
            c(node.body, st, "Statement");
        };
        base.SwitchStatement = function(node, st, c) {
            c(node.discriminant, st, "Expression");
            for (var i = 0; i < node.cases.length; ++i) {
                var cs = node.cases[i];
                if (cs.test) c(cs.test, st, "Expression");
                for (var j = 0; j < cs.consequent.length; ++j)
                    c(cs.consequent[j], st, "Statement");
            }
        };
        base.ReturnStatement = base.YieldExpression = function(node, st, c) {
            if (node.argument) c(node.argument, st, "Expression");
        };
        base.ThrowStatement = base.SpreadElement = function(node, st, c) {
            c(node.argument, st, "Expression");
        };
        base.TryStatement = function(node, st, c) {
            c(node.block, st, "Statement");
            if (node.handler) c(node.handler.body, st, "ScopeBody");
            if (node.finalizer) c(node.finalizer, st, "Statement");
        };
        base.WhileStatement = function(node, st, c) {
            c(node.test, st, "Expression");
            c(node.body, st, "Statement");
        };
        base.DoWhileStatement = base.WhileStatement;
        base.ForStatement = function(node, st, c) {
            if (node.init) c(node.init, st, "ForInit");
            if (node.test) c(node.test, st, "Expression");
            if (node.update) c(node.update, st, "Expression");
            c(node.body, st, "Statement");
        };
        base.ForInStatement = base.ForOfStatement = function(node, st, c) {
            c(node.left, st, "ForInit");
            c(node.right, st, "Expression");
            c(node.body, st, "Statement");
        };
        base.ForInit = function(node, st, c) {
            if (node.type == "VariableDeclaration") c(node, st);
            else c(node, st, "Expression");
        };
        base.DebuggerStatement = ignore;

        base.FunctionDeclaration = function(node, st, c) {
            c(node, st, "Function");
        };
        base.VariableDeclaration = function(node, st, c) {
            for (var i = 0; i < node.declarations.length; ++i) {
                var decl = node.declarations[i];
                if (decl.init) c(decl.init, st, "Expression");
            }
        };

        base.Function = function(node, st, c) {
            c(node.body, st, "ScopeBody");
        };
        base.ScopeBody = function(node, st, c) {
            c(node, st, "Statement");
        };

        base.Expression = skipThrough;
        base.ThisExpression = ignore;
        base.ArrayExpression = function(node, st, c) {
            for (var i = 0; i < node.elements.length; ++i) {
                var elt = node.elements[i];
                if (elt) c(elt, st, "Expression");
            }
        };
        base.ObjectExpression = function(node, st, c) {
            for (var i = 0; i < node.properties.length; ++i)
                c(node.properties[i], st);
        };
        base.FunctionExpression = base.ArrowFunctionExpression = base.FunctionDeclaration;
        base.SequenceExpression = base.TemplateLiteral = function(node, st, c) {
            for (var i = 0; i < node.expressions.length; ++i)
                c(node.expressions[i], st, "Expression");
        };
        base.UnaryExpression = base.UpdateExpression = function(node, st, c) {
            c(node.argument, st, "Expression");
        };
        base.BinaryExpression = base.AssignmentExpression = base.LogicalExpression = function(node, st, c) {
            c(node.left, st, "Expression");
            c(node.right, st, "Expression");
        };
        base.ConditionalExpression = function(node, st, c) {
            c(node.test, st, "Expression");
            c(node.consequent, st, "Expression");
            c(node.alternate, st, "Expression");
        };
        base.NewExpression = base.CallExpression = function(node, st, c) {
            c(node.callee, st, "Expression");
            if (node.arguments) for (var i = 0; i < node.arguments.length; ++i)
                c(node.arguments[i], st, "Expression");
        };
        base.MemberExpression = function(node, st, c) {
            c(node.object, st, "Expression");
            if (node.computed) c(node.property, st, "Expression");
        };
        base.Identifier = base.Literal = base.ExportDeclaration = base.ImportDeclaration = ignore;

        base.TaggedTemplateExpression = function(node, st, c) {
            c(node.tag, st, "Expression");
            c(node.quasi, st);
        };
        base.ClassDeclaration = base.ClassExpression = function(node, st, c) {
            if (node.superClass) c(node.superClass, st, "Expression");
            for (var i = 0; i < node.body.body.length; i++)
                c(node.body.body[i], st);
        };
        base.MethodDefinition = base.Property = function(node, st, c) {
            if (node.computed) c(node.key, st, "Expression");
            c(node.value, st, "Expression");
        };
        base.ComprehensionExpression = function(node, st, c) {
            for (var i = 0; i < node.blocks.length; i++)
                c(node.blocks[i].right, st, "Expression");
            c(node.body, st, "Expression");
        };

        // A custom walker that keeps track of the scope chain and the
        // variables defined in it.
        function makeScope(prev, isCatch) {
            return {vars: Object.create(null), prev: prev, isCatch: isCatch};
        }
        function normalScope(scope) {
            while (scope.isCatch) scope = scope.prev;
            return scope;
        }
        exports.scopeVisitor = exports.make({
            Function: function(node, scope, c) {
                var inner = makeScope(scope);
                for (var i = 0; i < node.params.length; ++i)
                    inner.vars[node.params[i].name] = {type: "argument", node: node.params[i]};
                if (node.id) {
                    var decl = node.type == "FunctionDeclaration";
                    (decl ? normalScope(scope) : inner).vars[node.id.name] =
                    {type: decl ? "function" : "function name", node: node.id};
                }
                c(node.body, inner, "ScopeBody");
            },
            TryStatement: function(node, scope, c) {
                c(node.block, scope, "Statement");
                if (node.handler) {
                    var inner = makeScope(scope, true);
                    inner.vars[node.handler.param.name] = {type: "catch clause", node: node.handler.param};
                    c(node.handler.body, inner, "ScopeBody");
                }
                if (node.finalizer) c(node.finalizer, scope, "Statement");
            },
            VariableDeclaration: function(node, scope, c) {
                var target = normalScope(scope);
                for (var i = 0; i < node.declarations.length; ++i) {
                    var decl = node.declarations[i];
                    target.vars[decl.id.name] = {type: "var", node: decl.id};
                    if (decl.init) c(decl.init, scope, "Expression");
                }
            }
        });

    });

},{}],34:[function(require,module,exports){
    /**
     * This file automatically generated from `pre-publish.js`.
     * Do not manually edit.
     */

    module.exports = {
        "area": true,
        "base": true,
        "br": true,
        "col": true,
        "embed": true,
        "hr": true,
        "img": true,
        "input": true,
        "keygen": true,
        "link": true,
        "menuitem": true,
        "meta": true,
        "param": true,
        "source": true,
        "track": true,
        "wbr": true
    };

},{}],35:[function(require,module,exports){
    'use strict';

    var detect = require('acorn-globals');
    var acorn = require('acorn');
    var walk = require('acorn/util/walk');

    module.exports = addWith

    /**
     * Mimic `with` as far as possible but at compile time
     *
     * @param {String} obj The object part of a with expression
     * @param {String} src The body of the with expression
     * @param {Array.<String>} exclude A list of variable names to explicitly exclude
     */
    function addWith(obj, src, exclude) {
        obj = obj + ''
        src = src + ''
        exclude = exclude || []
        exclude = exclude.concat(detect(obj).map(function (global) { return global.name; }))
        var vars = detect(src).map(function (global) { return global.name; })
            .filter(function (v) {
                return exclude.indexOf(v) === -1
            })

        if (vars.length === 0) return src

        var declareLocal = ''
        var local = 'locals_for_with'
        var result = 'result_of_with'
        if (/^[a-zA-Z0-9$_]+$/.test(obj)) {
            local = obj
        } else {
            while (vars.indexOf(local) != -1 || exclude.indexOf(local) != -1) {
                local += '_'
            }
            declareLocal = 'var ' + local + ' = (' + obj + ')'
        }
        while (vars.indexOf(result) != -1 || exclude.indexOf(result) != -1) {
            result += '_'
        }

        var inputVars = vars.map(function (v) {
            return JSON.stringify(v) + ' in ' + local + '?' +
                local + '.' + v + ':' +
                'typeof ' + v + '!=="undefined"?' + v + ':undefined'
        })

        src = '(function (' + vars.join(', ') + ') {' +
        src +
        '}.call(this' + inputVars.map(function (v) { return ',' + v; }).join('') + '))'

        return ';' + declareLocal + ';' + unwrapReturns(src, result) + ';'
    }

    /**
     * Take a self calling function, and unwrap it such that return inside the function
     * results in return outside the function
     *
     * @param {String} src    Some JavaScript code representing a self-calling function
     * @param {String} result A temporary variable to store the result in
     */
    function unwrapReturns(src, result) {
        var originalSource = src
        var hasReturn = false
        var ast = acorn.parse(src, {ecmaVersion: 6})
        var ref
        src = src.split('')

        // get a reference to the function that was inserted to add an inner context
        if ((ref = ast.body).length !== 1
            || (ref = ref[0]).type !== 'ExpressionStatement'
            || (ref = ref.expression).type !== 'CallExpression'
            || (ref = ref.callee).type !== 'MemberExpression' || ref.computed !== false || ref.property.name !== 'call'
            || (ref = ref.object).type !== 'FunctionExpression')
            throw new Error('AST does not seem to represent a self-calling function')
        var fn = ref

        walk.recursive(ast, null, {
            Function: function (node, st, c) {
                if (node === fn) {
                    c(node.body, st, "ScopeBody");
                }
            },
            ReturnStatement: function (node) {
                hasReturn = true
                replace(node, 'return {value: ' + source(node.argument) + '};');
            }
        });
        function source(node) {
            return src.slice(node.start, node.end).join('')
        }
        function replace(node, str) {
            for (var i = node.start; i < node.end; i++) {
                src[i] = ''
            }
            src[node.start] = str
        }
        if (!hasReturn) return originalSource
        else return 'var ' + result + '=' + src.join('') + ';if (' + result + ') return ' + result + '.value'
    }

},{"acorn":37,"acorn-globals":36,"acorn/util/walk":38}],36:[function(require,module,exports){
    module.exports=require(31)
},{"/Users/forbeslindesay/GitHub/jade/node_modules/constantinople/node_modules/acorn-globals/index.js":31,"acorn":37,"acorn/util/walk":38}],37:[function(require,module,exports){
    module.exports=require(32)
},{"/Users/forbeslindesay/GitHub/jade/node_modules/constantinople/node_modules/acorn-globals/node_modules/acorn/acorn.js":32}],38:[function(require,module,exports){
    module.exports=require(33)
},{"/Users/forbeslindesay/GitHub/jade/node_modules/constantinople/node_modules/acorn-globals/node_modules/acorn/util/walk.js":33}]},{},[4])(4)
});